From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pf1-f170.google.com (mail-pf1-f170.google.com [209.85.210.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 18C3736197D for ; Wed, 22 Jul 2026 16:25:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.170 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784737548; cv=none; b=a7meDGZPqYfbonhp6UTaBWQF81Fdhb/6x74xzA83MBU3JDfBjyKkiTJpbozxB9QfhOVK/S83KilkY5rWblYBmF0KpxIFoTKb1Y49HQawq/GrZCSy4aUIcpPhSOX/Ndd6kffjNQBLAL5MICqv+uPmhejWrT/BCmsqO0H5Pgg5ogE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784737548; c=relaxed/simple; bh=LGFj0f5U5Rg6b5unu6LCSaSHumocHaERBEdfox9875M=; h=Mime-Version:Content-Type:Date:Message-Id:Cc:Subject:From:To: References:In-Reply-To; b=ouGvo7e5IL21pjGteh9QgiSwiQg+27lmoy4IpldWDVRG5zsHHBBks5/3yhxWp7c0OepV8G4SV++oOYGAbszYN4OurO28Ps0hnUFPKgDYHkyCFl0M2Ib0V85nkCiL9Id5i0foWqO/u0jyguBiKAR5RSXdXnwoYZ09cnaFTRapsHI= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=etsalapatis.com; spf=pass smtp.mailfrom=etsalapatis.com; dkim=pass (2048-bit key) header.d=etsalapatis-com.20251104.gappssmtp.com header.i=@etsalapatis-com.20251104.gappssmtp.com header.b=K7n4tdvj; arc=none smtp.client-ip=209.85.210.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=etsalapatis.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=etsalapatis.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=etsalapatis-com.20251104.gappssmtp.com header.i=@etsalapatis-com.20251104.gappssmtp.com header.b="K7n4tdvj" Received: by mail-pf1-f170.google.com with SMTP id d2e1a72fcca58-8487b7b3fc8so13018759b3a.3 for ; Wed, 22 Jul 2026 09:25:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=etsalapatis-com.20251104.gappssmtp.com; s=20251104; t=1784737546; x=1785342346; darn=vger.kernel.org; h=in-reply-to:references:to:from:subject:cc:message-id:date :content-type:content-transfer-encoding:mime-version:from:to:cc :subject:date:message-id:reply-to:content-type; bh=IZkomDUrGkXkXZb8BhxXqnqY3bkstvsHnN4dmShpkRM=; b=K7n4tdvjQ3AMaeNHQyIIOXB0GdIm5YLSgwT6QUuTKVUkgwoqt22rhUPhs2SVFFxvCU 1Baj+/W3vuTwrkrY8O2SYbFfInux/2oZTObxEvCWRcKRacwCR7+CThH9RdOm3xEqDqCb eyGhymQYSDJYLAsPxQixMoqcjO3ZOkLAlhdHU9IpXBfo5R9ZF1QOWUSrtzqTVWPg0HnJ lUQ99a6ZaMw3u42zPOAhdEuV8Ij36NXIN4NwtwyJ9sTHVH4xx0EAvcLFaBDAQLIKUEdr j7dQDYcsZTAloQfIp89e6mzLkhdZEJEsI0s2cSIWetLHmcFKH6b+G67lX6SjAQwKWL4J mffw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784737546; x=1785342346; h=in-reply-to:references:to:from:subject:cc:message-id:date :content-type:content-transfer-encoding:mime-version:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=IZkomDUrGkXkXZb8BhxXqnqY3bkstvsHnN4dmShpkRM=; b=BjQMw8+DbGhw708Vez3jSr4x3JXHmmPZEAfGRXNqTriaZzVR82mfn9sastD/Jy/0MO k3B0EFq9RCIB7t63zzCUaoaXgFDa3uSa0kJUyw/fofCZvT0tCM7v7xYrIAONoTuNe9aU BX1NJ8FazjyV/6vz/B9cbvdo9Hsb2jjVtO+mlJuRZ9F7crdxWgqrijPshyrzhdkHSmix 34jJhv2t4V2Y+MHeeU3GXGW377ZCBMWRlkfbksEuGl5ux8bFaf8rCyRYNJ5yvC+2AxPi gjCsgZW8jISPC+ChmRNBIIj88jGnK8k2Yw9Bg3GPKRSrCgcmWbtJzmWnIfhMwNhRga/h 9INw== X-Forwarded-Encrypted: i=1; AHgh+Rr31Mdx8Oycb+TSzN4Ys56Yf2LF7TGeGDJndIUkSMn+9DFsmBD78wxivi29kNdK4ug685STnfzM9Hwg0/k=@vger.kernel.org X-Gm-Message-State: AOJu0Yzu0og2saoyCodggVw+64JHGPAaW0iuHFuKKw3PFTL9m6RUC5aP BYwI1Cf9QqQckyN4y7XGQrWEJfbA1TWq7RrRQQn9SDWozO0ixKvISUk9+OEPZML3fhY= X-Gm-Gg: AR+sD11AqpUy8WBq6xTw/puBfTzgnzUt+jQjkRGF/VwXlB8DxBBo4HBqW29RXx90Zjz NMLiKZ3AnNf+BCEInBOzjjuRTV0s+WZxxQfUl3BQrLShGKapUgFIfY9hvEw8vjoADlluJx/IM5X v2mEWJ+IN2kVM8rf0HTz/LDZWtII4hZ+50cG5kY4sUmzyndv84M5NxQplCaIyiXaj7gKhbzQU24 yEkPwzdIVe5d9nBpeWh2rRl5e9XjVGXsHviRZse9E1kK1qq/0wJxe9IFaOYsS40wn2SnBk1xYMf 2Q+H1gql7Ix0bYx1/JbTUd5rDGDo9YAVRiAehP7TWVNYUqEjJLwlTTbGe6wMbaWNyUjnjJVeXJf OS29YgbbWfZhPomerV37UvwW+LozUEP+Ih60/vUMlzPwheUSTPUoZ09uAiIIzFT4CAV6aBHOvUx xAn8TLMS0IAQN8Q3cf1dM475k2q+qgg43JEWmzsWLNNg== X-Received: by 2002:a05:6a00:cc7:b0:847:9565:7a58 with SMTP id d2e1a72fcca58-84c294d43f6mr23330487b3a.48.1784737546418; Wed, 22 Jul 2026 09:25:46 -0700 (PDT) Received: from localhost (107-190-31-17.cpe.teksavvy.com. [107.190.31.17]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e17297f8asm1641552b3a.25.2026.07.22.09.25.45 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 22 Jul 2026 09:25:46 -0700 (PDT) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8 Date: Wed, 22 Jul 2026 12:25:44 -0400 Message-Id: Cc: "Alexei Starovoitov" , "Daniel Borkmann" , "Andrii Nakryiko" , "Eduard Zingerman" , "Kumar Kartikeya Dwivedi" , "Martin KaFai Lau" , "Yonghong Song" , "Song Liu" , "Jiri Olsa" , "Pu Lehui" Subject: Re: [PATCH bpf v6 2/4] bpf: Fix UAF due to missing link type check in mprog From: "Emil Tsalapatis" To: "Pu Lehui" , , , "Amery Hung" , "Emil Tsalapatis" , "Mykyta Yatsenko" X-Mailer: aerc 0.21.0-0-g5549850facc2 References: <20260722072326.1545677-1-pulehui@huaweicloud.com> <20260722072326.1545677-3-pulehui@huaweicloud.com> In-Reply-To: <20260722072326.1545677-3-pulehui@huaweicloud.com> On Wed Jul 22, 2026 at 3:23 AM EDT, Pu Lehui wrote: > From: Pu Lehui > > In bpf_mprog_link, the code does not check the link->type first before > dereferencing link->prog->type. This missing validation allows a user to > pass an abnormal non-netkit or non-tcx link via relative_fd. If doing > BPF_LINK_UPDATE on the abnormal link, it can trigger a UAF issue. > > CPU0 CPU1 > netkit_link_prog_attach > bpf_mprog_attach > bpf_mprog_tuple_relative > bpf_mprog_link > /* non-netkit or non-tcx link */ > link =3D bpf_link_get_from_fd(id_or_fd); > BPF_LINK_UPDATE on relative lin= k > ... > old_prog =3D xchg(&link->link.p= rog, new_prog); > bpf_prog_put(old_prog); > if (type && link->prog->type !=3D type) <-- trigger UAF > > The reason for the UAF is that each subsystem provides its own > protection for link->prog. Since there is no cross subsystem protection > (if not considering the RCU of prog tear down), dereferencing the prog > of an anchor link that does not belong to the current subsystem is not > safe: it may have been freed. Therefore, we need to validate link->type > to reject foreign anchors. > > Fix this by strictly validating link->type in bpf_mprog_link against the > expected link type. mprog APIs is also adjusted to accept and pass down > the expected link type. Meanwhile, add a comment explaining that when > ptype =3D=3D UNSPEC in bpf_mprog_detach, it acts as a wildcard. > > Fixes: 053c8e1f235d ("bpf: Add generic attach/detach/query API for multi-= progs") > Reported-by: Sashiko > Reviewed-by: Amery Hung > Signed-off-by: Pu Lehui Unless I'm missing something, at the very least there's no need for the extra argument in detach() since we pass UNSPEC to bpf_mprog_link() and never use it otherwise. Even for the attach case, aren't we required to provide a new link for attachment, and so already have the ltype available to test against? > --- > drivers/net/netkit.c | 13 ++++++------- > include/linux/bpf_mprog.h | 6 ++++-- > kernel/bpf/mprog.c | 23 ++++++++++++++--------- > kernel/bpf/tcx.c | 13 ++++++------- > 4 files changed, 30 insertions(+), 25 deletions(-) > > diff --git a/drivers/net/netkit.c b/drivers/net/netkit.c > index a3931cd82132..99ddf2befb23 100644 > --- a/drivers/net/netkit.c > +++ b/drivers/net/netkit.c > @@ -768,7 +768,7 @@ int netkit_prog_attach(const union bpf_attr *attr, st= ruct bpf_prog *prog) > } > ret =3D bpf_mprog_attach(entry, &entry_new, prog, NULL, replace_prog, > attr->attach_flags, attr->relative_fd, > - attr->expected_revision); > + attr->expected_revision, BPF_LINK_TYPE_NETKIT); > if (!ret) { > if (entry !=3D entry_new) { > netkit_entry_update(dev, entry_new); > @@ -802,7 +802,7 @@ int netkit_prog_detach(const union bpf_attr *attr, st= ruct bpf_prog *prog) > goto out; > } > ret =3D bpf_mprog_detach(entry, &entry_new, prog, NULL, attr->attach_fl= ags, > - attr->relative_fd, attr->expected_revision); > + attr->relative_fd, attr->expected_revision, BPF_LINK_TYPE_NETK= IT); > if (!ret) { > if (!bpf_mprog_total(entry_new)) > entry_new =3D NULL; > @@ -850,7 +850,7 @@ static int netkit_link_prog_attach(struct bpf_link *l= ink, u32 flags, > ASSERT_RTNL(); > entry =3D netkit_entry_fetch(dev, true); > ret =3D bpf_mprog_attach(entry, &entry_new, link->prog, link, NULL, fla= gs, > - id_or_fd, revision); > + id_or_fd, revision, BPF_LINK_TYPE_NETKIT); > if (!ret) { > if (entry !=3D entry_new) { > netkit_entry_update(dev, entry_new); > @@ -877,7 +877,7 @@ static void netkit_link_release(struct bpf_link *link= ) > ret =3D -ENOENT; > goto out; > } > - ret =3D bpf_mprog_detach(entry, &entry_new, link->prog, link, 0, 0, 0); > + ret =3D bpf_mprog_detach(entry, &entry_new, link->prog, link, 0, 0, 0, = BPF_LINK_TYPE_NETKIT); > if (!ret) { > if (!bpf_mprog_total(entry_new)) > entry_new =3D NULL; > @@ -919,9 +919,8 @@ static int netkit_link_update(struct bpf_link *link, = struct bpf_prog *nprog, > ret =3D -ENOENT; > goto out; > } > - ret =3D bpf_mprog_attach(entry, &entry_new, nprog, link, oprog, > - BPF_F_REPLACE | BPF_F_ID, > - link->prog->aux->id, 0); > + ret =3D bpf_mprog_attach(entry, &entry_new, nprog, link, oprog, BPF_F_R= EPLACE | BPF_F_ID, > + link->prog->aux->id, 0, BPF_LINK_TYPE_NETKIT); > if (!ret) { > WARN_ON_ONCE(entry !=3D entry_new); > oprog =3D xchg(&link->prog, nprog); > diff --git a/include/linux/bpf_mprog.h b/include/linux/bpf_mprog.h > index 0b9f4caeeb0a..1fbe1a923968 100644 > --- a/include/linux/bpf_mprog.h > +++ b/include/linux/bpf_mprog.h > @@ -321,12 +321,14 @@ int bpf_mprog_attach(struct bpf_mprog_entry *entry, > struct bpf_mprog_entry **entry_new, > struct bpf_prog *prog_new, struct bpf_link *link, > struct bpf_prog *prog_old, > - u32 flags, u32 id_or_fd, u64 revision); > + u32 flags, u32 id_or_fd, u64 revision, > + enum bpf_link_type expected_link_type); > =20 > int bpf_mprog_detach(struct bpf_mprog_entry *entry, > struct bpf_mprog_entry **entry_new, > struct bpf_prog *prog, struct bpf_link *link, > - u32 flags, u32 id_or_fd, u64 revision); > + u32 flags, u32 id_or_fd, u64 revision, > + enum bpf_link_type expected_link_type); > =20 > int bpf_mprog_query(const union bpf_attr *attr, union bpf_attr __user *u= attr, > struct bpf_mprog_entry *entry); > diff --git a/kernel/bpf/mprog.c b/kernel/bpf/mprog.c > index 1394168062e8..b4a1b35ff569 100644 > --- a/kernel/bpf/mprog.c > +++ b/kernel/bpf/mprog.c > @@ -6,7 +6,7 @@ > =20 > static int bpf_mprog_link(struct bpf_tuple *tuple, > u32 id_or_fd, u32 flags, > - enum bpf_prog_type type) > + enum bpf_link_type type) > { > struct bpf_link *link =3D ERR_PTR(-EINVAL); > bool id =3D flags & BPF_F_ID; > @@ -17,7 +17,7 @@ static int bpf_mprog_link(struct bpf_tuple *tuple, > link =3D bpf_link_get_from_fd(id_or_fd); > if (IS_ERR(link)) > return PTR_ERR(link); > - if (type && link->prog->type !=3D type) { > + if (type && link->type !=3D type) { > bpf_link_put(link); > return -EINVAL; > } > @@ -52,21 +52,22 @@ static int bpf_mprog_prog(struct bpf_tuple *tuple, > =20 > static int bpf_mprog_tuple_relative(struct bpf_tuple *tuple, > u32 id_or_fd, u32 flags, > - enum bpf_prog_type type) > + enum bpf_link_type ltype, > + enum bpf_prog_type ptype) > { > bool link =3D flags & BPF_F_LINK; > bool id =3D flags & BPF_F_ID; > =20 > memset(tuple, 0, sizeof(*tuple)); > if (link) > - return bpf_mprog_link(tuple, id_or_fd, flags, type); > + return bpf_mprog_link(tuple, id_or_fd, flags, ltype); > /* If no relevant flag is set and no id_or_fd was passed, then > * tuple link/prog is just NULLed. This is the case when before/ > * after selects first/last position without passing fd. > */ > if (!id && !id_or_fd) > return 0; > - return bpf_mprog_prog(tuple, id_or_fd, flags, type); > + return bpf_mprog_prog(tuple, id_or_fd, flags, ptype); > } > =20 > static void bpf_mprog_tuple_put(struct bpf_tuple *tuple) > @@ -226,7 +227,8 @@ int bpf_mprog_attach(struct bpf_mprog_entry *entry, > struct bpf_mprog_entry **entry_new, > struct bpf_prog *prog_new, struct bpf_link *link, > struct bpf_prog *prog_old, > - u32 flags, u32 id_or_fd, u64 revision) > + u32 flags, u32 id_or_fd, u64 revision, > + enum bpf_link_type expected_link_type) > { > struct bpf_tuple rtuple, ntuple =3D { > .prog =3D prog_new, > @@ -243,6 +245,7 @@ int bpf_mprog_attach(struct bpf_mprog_entry *entry, > return -EEXIST; > ret =3D bpf_mprog_tuple_relative(&rtuple, id_or_fd, > flags & ~BPF_F_REPLACE, > + expected_link_type, > prog_new->type); > if (ret) > return ret; > @@ -328,7 +331,8 @@ static int bpf_mprog_fetch(struct bpf_mprog_entry *en= try, > int bpf_mprog_detach(struct bpf_mprog_entry *entry, > struct bpf_mprog_entry **entry_new, > struct bpf_prog *prog, struct bpf_link *link, > - u32 flags, u32 id_or_fd, u64 revision) > + u32 flags, u32 id_or_fd, u64 revision, > + enum bpf_link_type expected_link_type) > { > struct bpf_tuple rtuple, dtuple =3D { > .prog =3D prog, > @@ -343,8 +347,9 @@ int bpf_mprog_detach(struct bpf_mprog_entry *entry, > if (!bpf_mprog_total(entry)) > return -ENOENT; > ret =3D bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags, > - prog ? prog->type : > - BPF_PROG_TYPE_UNSPEC); > + expected_link_type, > + /* Use UNSPEC as wildcard when prog is NULL */ > + prog ? prog->type : BPF_PROG_TYPE_UNSPEC); > if (ret) > return ret; > if (dtuple.prog) { > diff --git a/kernel/bpf/tcx.c b/kernel/bpf/tcx.c > index 02db0113b8e7..f208cef13a98 100644 > --- a/kernel/bpf/tcx.c > +++ b/kernel/bpf/tcx.c > @@ -38,7 +38,7 @@ int tcx_prog_attach(const union bpf_attr *attr, struct = bpf_prog *prog) > } > ret =3D bpf_mprog_attach(entry, &entry_new, prog, NULL, replace_prog, > attr->attach_flags, attr->relative_fd, > - attr->expected_revision); > + attr->expected_revision, BPF_LINK_TYPE_TCX); > if (!ret) { > if (entry !=3D entry_new) { > tcx_entry_update(dev, entry_new, ingress); > @@ -76,7 +76,7 @@ int tcx_prog_detach(const union bpf_attr *attr, struct = bpf_prog *prog) > goto out; > } > ret =3D bpf_mprog_detach(entry, &entry_new, prog, NULL, attr->attach_fl= ags, > - attr->relative_fd, attr->expected_revision); > + attr->relative_fd, attr->expected_revision, BPF_LINK_TYPE_TCX)= ; > if (!ret) { > if (!tcx_entry_is_active(entry_new)) > entry_new =3D NULL; > @@ -152,7 +152,7 @@ static int tcx_link_prog_attach(struct bpf_link *link= , u32 flags, u32 id_or_fd, > if (!entry) > return -ENOMEM; > ret =3D bpf_mprog_attach(entry, &entry_new, link->prog, link, NULL, fla= gs, > - id_or_fd, revision); > + id_or_fd, revision, BPF_LINK_TYPE_TCX); > if (!ret) { > if (entry !=3D entry_new) { > tcx_entry_update(dev, entry_new, ingress); > @@ -183,7 +183,7 @@ static void tcx_link_release(struct bpf_link *link) > ret =3D -ENOENT; > goto out; > } > - ret =3D bpf_mprog_detach(entry, &entry_new, link->prog, link, 0, 0, 0); > + ret =3D bpf_mprog_detach(entry, &entry_new, link->prog, link, 0, 0, 0, = BPF_LINK_TYPE_TCX); > if (!ret) { > if (!tcx_entry_is_active(entry_new)) > entry_new =3D NULL; > @@ -229,9 +229,8 @@ static int tcx_link_update(struct bpf_link *link, str= uct bpf_prog *nprog, > ret =3D -ENOENT; > goto out; > } > - ret =3D bpf_mprog_attach(entry, &entry_new, nprog, link, oprog, > - BPF_F_REPLACE | BPF_F_ID, > - link->prog->aux->id, 0); > + ret =3D bpf_mprog_attach(entry, &entry_new, nprog, link, oprog, BPF_F_R= EPLACE | BPF_F_ID, > + link->prog->aux->id, 0, BPF_LINK_TYPE_TCX); > if (!ret) { > WARN_ON_ONCE(entry !=3D entry_new); > oprog =3D xchg(&link->prog, nprog);