BPF List
 help / color / mirror / Atom feed
* [PATCH bpf v4] bpf: Fix UAF when reading prog type in bpf_mprog_link
@ 2026-07-28  7:42 Pu Lehui
  2026-08-03 19:00 ` Daniel Borkmann
  0 siblings, 1 reply; 5+ messages in thread
From: Pu Lehui @ 2026-07-28  7:42 UTC (permalink / raw)
  To: bpf, linux-kernel
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Yonghong Song, Song Liu, Jiri Olsa, Emil Tsalapatis, Pu Lehui,
	Pu Lehui

From: Pu Lehui <pulehui@huawei.com>

In bpf_mprog_link, the code currently allows a user to pass an abnormal
non-netkit or non-tcx link via relative_fd. If a concurrent
BPF_LINK_UPDATE is performed on this abnormal link before dereferencing
link->prog->type in bpf_mprog_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 = bpf_link_get_from_fd(id_or_fd);
                                          BPF_LINK_UPDATE on relative link
                                          ...
                                          old_prog = xchg(&link->link.prog, new_prog);
                                          bpf_prog_put(old_prog);
  if (type && link->prog->type != 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.

To resolve this, we access link->prog under RCU protection to safely
fetch the pointer and guarantee its lifetime during the type check.
Meanwhile, add a comment explaining that when ptype == 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 <sashiko-bot@kernel.org>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
Signed-off-by: Pu Lehui <pulehui@huawei.com>
---
v4:
- Access prog->type under rcu protection to simplify the repair logic,
  and let unconditional detachment make sense when the bare prog or
  link->prog being detached is NULL.
- Add Reviewed-by tag by Emil.
- Separate from patchset [0]. (Andrii)

Link: https://lore.kernel.org/bpf/f87b53c0-8f00-45a6-82db-8242fa9b143f@huaweicloud.com [0]

v3: https://lore.kernel.org/bpf/20260722072326.1545677-3-pulehui@huaweicloud.com
- BPF_F_LINK flag set means the relative id_or_fd is a link, not the object being
  attached. We can not get the link while attach a bare prog with relative link.
  So passing expected link type from callers of bpf_mprog_attach/detach. (Sashiko)
- Add comment to explain that why ptype == UNSPEC. (Emil)

v2: https://lore.kernel.org/bpf/20260721041048.1394085-3-pulehui@huaweicloud.com
- Improve commit msg for patch 2. (Amery)

v1: https://lore.kernel.org/bpf/20260720134547.1289964-3-pulehui@huaweicloud.com

 kernel/bpf/mprog.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/kernel/bpf/mprog.c b/kernel/bpf/mprog.c
index 1394168062e8..af3e6c1c6a1f 100644
--- a/kernel/bpf/mprog.c
+++ b/kernel/bpf/mprog.c
@@ -10,6 +10,8 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
 {
 	struct bpf_link *link = ERR_PTR(-EINVAL);
 	bool id = flags & BPF_F_ID;
+	bool type_mismatch = false;
+	struct bpf_prog *prog;
 
 	if (id)
 		link = bpf_link_by_id(id_or_fd);
@@ -17,13 +19,20 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
 		link = bpf_link_get_from_fd(id_or_fd);
 	if (IS_ERR(link))
 		return PTR_ERR(link);
-	if (type && link->prog->type != type) {
+
+	rcu_read_lock();
+	prog = READ_ONCE(link->prog);
+	if (!prog || (type && prog->type != type))
+		type_mismatch = true;
+	rcu_read_unlock();
+
+	if (type_mismatch) {
 		bpf_link_put(link);
 		return -EINVAL;
 	}
 
 	tuple->link = link;
-	tuple->prog = link->prog;
+	tuple->prog = prog;
 	return 0;
 }
 
@@ -343,8 +352,8 @@ int bpf_mprog_detach(struct bpf_mprog_entry *entry,
 	if (!bpf_mprog_total(entry))
 		return -ENOENT;
 	ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags,
-				       prog ? prog->type :
-				       BPF_PROG_TYPE_UNSPEC);
+				       /* Use UNSPEC as wildcard when prog is NULL */
+				       prog ? prog->type : BPF_PROG_TYPE_UNSPEC);
 	if (ret)
 		return ret;
 	if (dtuple.prog) {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf v4] bpf: Fix UAF when reading prog type in bpf_mprog_link
  2026-07-28  7:42 [PATCH bpf v4] bpf: Fix UAF when reading prog type in bpf_mprog_link Pu Lehui
@ 2026-08-03 19:00 ` Daniel Borkmann
  2026-08-03 19:14   ` Kumar Kartikeya Dwivedi
  2026-08-05  4:18   ` Pu Lehui
  0 siblings, 2 replies; 5+ messages in thread
From: Daniel Borkmann @ 2026-08-03 19:00 UTC (permalink / raw)
  To: Pu Lehui, bpf, linux-kernel
  Cc: Alexei Starovoitov, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Martin KaFai Lau, Yonghong Song,
	Song Liu, Jiri Olsa, Emil Tsalapatis, Pu Lehui

On 7/28/26 9:42 AM, Pu Lehui wrote:
> From: Pu Lehui <pulehui@huawei.com>
> 
> In bpf_mprog_link, the code currently allows a user to pass an abnormal
> non-netkit or non-tcx link via relative_fd. If a concurrent
> BPF_LINK_UPDATE is performed on this abnormal link before dereferencing
> link->prog->type in bpf_mprog_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 = bpf_link_get_from_fd(id_or_fd);
>                                            BPF_LINK_UPDATE on relative link
>                                            ...
>                                            old_prog = xchg(&link->link.prog, new_prog);
>                                            bpf_prog_put(old_prog);
>    if (type && link->prog->type != 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.
> 
> To resolve this, we access link->prog under RCU protection to safely
> fetch the pointer and guarantee its lifetime during the type check.
> Meanwhile, add a comment explaining that when ptype == 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 <sashiko-bot@kernel.org>
> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
> Signed-off-by: Pu Lehui <pulehui@huawei.com>
> ---
> v4:
> - Access prog->type under rcu protection to simplify the repair logic,
>    and let unconditional detachment make sense when the bare prog or
>    link->prog being detached is NULL.
> - Add Reviewed-by tag by Emil.
> - Separate from patchset [0]. (Andrii)
> 
> Link: https://lore.kernel.org/bpf/f87b53c0-8f00-45a6-82db-8242fa9b143f@huaweicloud.com [0]
> 
> v3: https://lore.kernel.org/bpf/20260722072326.1545677-3-pulehui@huaweicloud.com
> - BPF_F_LINK flag set means the relative id_or_fd is a link, not the object being
>    attached. We can not get the link while attach a bare prog with relative link.
>    So passing expected link type from callers of bpf_mprog_attach/detach. (Sashiko)
> - Add comment to explain that why ptype == UNSPEC. (Emil)
> 
> v2: https://lore.kernel.org/bpf/20260721041048.1394085-3-pulehui@huaweicloud.com
> - Improve commit msg for patch 2. (Amery)
> 
> v1: https://lore.kernel.org/bpf/20260720134547.1289964-3-pulehui@huaweicloud.com
> 
>   kernel/bpf/mprog.c | 17 +++++++++++++----
>   1 file changed, 13 insertions(+), 4 deletions(-)
> 
> diff --git a/kernel/bpf/mprog.c b/kernel/bpf/mprog.c
> index 1394168062e8..af3e6c1c6a1f 100644
> --- a/kernel/bpf/mprog.c
> +++ b/kernel/bpf/mprog.c
> @@ -10,6 +10,8 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
>   {
>   	struct bpf_link *link = ERR_PTR(-EINVAL);
>   	bool id = flags & BPF_F_ID;
> +	bool type_mismatch = false;
> +	struct bpf_prog *prog;
>   
>   	if (id)
>   		link = bpf_link_by_id(id_or_fd);
> @@ -17,13 +19,20 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
>   		link = bpf_link_get_from_fd(id_or_fd);
>   	if (IS_ERR(link))
>   		return PTR_ERR(link);
> -	if (type && link->prog->type != type) {
> +
> +	rcu_read_lock();
> +	prog = READ_ONCE(link->prog);
> +	if (!prog || (type && prog->type != type))
> +		type_mismatch = true;
> +	rcu_read_unlock();

Hm, what about progs under rcu_read_lock_trace cases, wouldn't the UAF
still be there?

Even though the diff is smaller, I'd kind of lean towards link->type
testing since this addresses the underlying issue and avoids touching
the prog completely.. do you want me to look into it and also add a
BPF selftest to it as patch 2/2?

> +	if (type_mismatch) {
>   		bpf_link_put(link);
>   		return -EINVAL;
>   	}
>   
>   	tuple->link = link;
> -	tuple->prog = link->prog;
> +	tuple->prog = prog;
>   	return 0;
>   }
>   
> @@ -343,8 +352,8 @@ int bpf_mprog_detach(struct bpf_mprog_entry *entry,
>   	if (!bpf_mprog_total(entry))
>   		return -ENOENT;
>   	ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags,
> -				       prog ? prog->type :
> -				       BPF_PROG_TYPE_UNSPEC);
> +				       /* Use UNSPEC as wildcard when prog is NULL */
> +				       prog ? prog->type : BPF_PROG_TYPE_UNSPEC);
>   	if (ret)
>   		return ret;
>   	if (dtuple.prog) {


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf v4] bpf: Fix UAF when reading prog type in bpf_mprog_link
  2026-08-03 19:00 ` Daniel Borkmann
@ 2026-08-03 19:14   ` Kumar Kartikeya Dwivedi
  2026-08-05  4:11     ` Pu Lehui
  2026-08-05  4:18   ` Pu Lehui
  1 sibling, 1 reply; 5+ messages in thread
From: Kumar Kartikeya Dwivedi @ 2026-08-03 19:14 UTC (permalink / raw)
  To: Daniel Borkmann, Pu Lehui, bpf, linux-kernel
  Cc: Alexei Starovoitov, Andrii Nakryiko, Eduard Zingerman,
	Martin KaFai Lau, Yonghong Song, Song Liu, Jiri Olsa,
	Emil Tsalapatis, Pu Lehui

On Mon Aug 3, 2026 at 9:00 PM CEST, Daniel Borkmann wrote:
> On 7/28/26 9:42 AM, Pu Lehui wrote:
>> From: Pu Lehui <pulehui@huawei.com>
>>
>> In bpf_mprog_link, the code currently allows a user to pass an abnormal
>> non-netkit or non-tcx link via relative_fd. If a concurrent
>> BPF_LINK_UPDATE is performed on this abnormal link before dereferencing
>> link->prog->type in bpf_mprog_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 = bpf_link_get_from_fd(id_or_fd);
>>                                            BPF_LINK_UPDATE on relative link
>>                                            ...
>>                                            old_prog = xchg(&link->link.prog, new_prog);
>>                                            bpf_prog_put(old_prog);
>>    if (type && link->prog->type != 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.
>>
>> To resolve this, we access link->prog under RCU protection to safely
>> fetch the pointer and guarantee its lifetime during the type check.
>> Meanwhile, add a comment explaining that when ptype == 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 <sashiko-bot@kernel.org>
>> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
>> Signed-off-by: Pu Lehui <pulehui@huawei.com>
>> ---
>> v4:
>> - Access prog->type under rcu protection to simplify the repair logic,
>>    and let unconditional detachment make sense when the bare prog or
>>    link->prog being detached is NULL.
>> - Add Reviewed-by tag by Emil.
>> - Separate from patchset [0]. (Andrii)
>>
>> Link: https://lore.kernel.org/bpf/f87b53c0-8f00-45a6-82db-8242fa9b143f@huaweicloud.com [0]
>>
>> v3: https://lore.kernel.org/bpf/20260722072326.1545677-3-pulehui@huaweicloud.com
>> - BPF_F_LINK flag set means the relative id_or_fd is a link, not the object being
>>    attached. We can not get the link while attach a bare prog with relative link.
>>    So passing expected link type from callers of bpf_mprog_attach/detach. (Sashiko)
>> - Add comment to explain that why ptype == UNSPEC. (Emil)
>>
>> v2: https://lore.kernel.org/bpf/20260721041048.1394085-3-pulehui@huaweicloud.com
>> - Improve commit msg for patch 2. (Amery)
>>
>> v1: https://lore.kernel.org/bpf/20260720134547.1289964-3-pulehui@huaweicloud.com
>>
>>   kernel/bpf/mprog.c | 17 +++++++++++++----
>>   1 file changed, 13 insertions(+), 4 deletions(-)
>>
>> diff --git a/kernel/bpf/mprog.c b/kernel/bpf/mprog.c
>> index 1394168062e8..af3e6c1c6a1f 100644
>> --- a/kernel/bpf/mprog.c
>> +++ b/kernel/bpf/mprog.c
>> @@ -10,6 +10,8 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
>>   {
>>   	struct bpf_link *link = ERR_PTR(-EINVAL);
>>   	bool id = flags & BPF_F_ID;
>> +	bool type_mismatch = false;
>> +	struct bpf_prog *prog;
>>
>>   	if (id)
>>   		link = bpf_link_by_id(id_or_fd);
>> @@ -17,13 +19,20 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
>>   		link = bpf_link_get_from_fd(id_or_fd);
>>   	if (IS_ERR(link))
>>   		return PTR_ERR(link);
>> -	if (type && link->prog->type != type) {
>> +
>> +	rcu_read_lock();
>> +	prog = READ_ONCE(link->prog);
>> +	if (!prog || (type && prog->type != type))
>> +		type_mismatch = true;
>> +	rcu_read_unlock();
>
> Hm, what about progs under rcu_read_lock_trace cases, wouldn't the UAF
> still be there?
>
> Even though the diff is smaller, I'd kind of lean towards link->type
> testing since this addresses the underlying issue and avoids touching
> the prog completely.. do you want me to look into it and also add a
> BPF selftest to it as patch 2/2?
>

I think Pu's implication was that an RCU tasks trace GP would imply RCU gp, thus
using RCU read lock for protection in both cases would be sufficient for the
link's free path to wait for this reader. More context is available in [0]. I do
think this merits a comment for clarity, even in [0] I explicitly commented
about it everywhere even when it felt unnecessary.

I didn't closely follow this set of fixes (those in addition to this) yet but in
general, we also need to be careful about tracepoint BPF links. Those will only
wait for SRCU gp, which is not implied for rcu_read_lock() etc., so will need
their own distinct critical section. The fix pertaining to that was in [1].

  [0]: https://lore.kernel.org/bpf/20260407162234.785270-1-memxor@gmail.com
  [1]: https://lore.kernel.org/bpf/20260331211021.1632902-2-memxor@gmail.com

>> +	if (type_mismatch) {
>>   		bpf_link_put(link);
>>   		return -EINVAL;
>>   	}
>>
>>   	tuple->link = link;
>> -	tuple->prog = link->prog;
>> +	tuple->prog = prog;
>>   	return 0;
>>   }
>>
>> @@ -343,8 +352,8 @@ int bpf_mprog_detach(struct bpf_mprog_entry *entry,
>>   	if (!bpf_mprog_total(entry))
>>   		return -ENOENT;
>>   	ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags,
>> -				       prog ? prog->type :
>> -				       BPF_PROG_TYPE_UNSPEC);
>> +				       /* Use UNSPEC as wildcard when prog is NULL */
>> +				       prog ? prog->type : BPF_PROG_TYPE_UNSPEC);
>>   	if (ret)
>>   		return ret;
>>   	if (dtuple.prog) {


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf v4] bpf: Fix UAF when reading prog type in bpf_mprog_link
  2026-08-03 19:14   ` Kumar Kartikeya Dwivedi
@ 2026-08-05  4:11     ` Pu Lehui
  0 siblings, 0 replies; 5+ messages in thread
From: Pu Lehui @ 2026-08-05  4:11 UTC (permalink / raw)
  To: Kumar Kartikeya Dwivedi, Daniel Borkmann, bpf, linux-kernel
  Cc: Alexei Starovoitov, Andrii Nakryiko, Eduard Zingerman,
	Martin KaFai Lau, Yonghong Song, Song Liu, Jiri Olsa,
	Emil Tsalapatis, Pu Lehui



On 2026/8/4 3:14, Kumar Kartikeya Dwivedi wrote:
> On Mon Aug 3, 2026 at 9:00 PM CEST, Daniel Borkmann wrote:
>> On 7/28/26 9:42 AM, Pu Lehui wrote:
>>> From: Pu Lehui <pulehui@huawei.com>
>>>
>>> In bpf_mprog_link, the code currently allows a user to pass an abnormal
>>> non-netkit or non-tcx link via relative_fd. If a concurrent
>>> BPF_LINK_UPDATE is performed on this abnormal link before dereferencing
>>> link->prog->type in bpf_mprog_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 = bpf_link_get_from_fd(id_or_fd);
>>>                                             BPF_LINK_UPDATE on relative link
>>>                                             ...
>>>                                             old_prog = xchg(&link->link.prog, new_prog);
>>>                                             bpf_prog_put(old_prog);
>>>     if (type && link->prog->type != 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.
>>>
>>> To resolve this, we access link->prog under RCU protection to safely
>>> fetch the pointer and guarantee its lifetime during the type check.
>>> Meanwhile, add a comment explaining that when ptype == 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 <sashiko-bot@kernel.org>
>>> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
>>> Signed-off-by: Pu Lehui <pulehui@huawei.com>
>>> ---
>>> v4:
>>> - Access prog->type under rcu protection to simplify the repair logic,
>>>     and let unconditional detachment make sense when the bare prog or
>>>     link->prog being detached is NULL.
>>> - Add Reviewed-by tag by Emil.
>>> - Separate from patchset [0]. (Andrii)
>>>
>>> Link: https://lore.kernel.org/bpf/f87b53c0-8f00-45a6-82db-8242fa9b143f@huaweicloud.com [0]
>>>
>>> v3: https://lore.kernel.org/bpf/20260722072326.1545677-3-pulehui@huaweicloud.com
>>> - BPF_F_LINK flag set means the relative id_or_fd is a link, not the object being
>>>     attached. We can not get the link while attach a bare prog with relative link.
>>>     So passing expected link type from callers of bpf_mprog_attach/detach. (Sashiko)
>>> - Add comment to explain that why ptype == UNSPEC. (Emil)
>>>
>>> v2: https://lore.kernel.org/bpf/20260721041048.1394085-3-pulehui@huaweicloud.com
>>> - Improve commit msg for patch 2. (Amery)
>>>
>>> v1: https://lore.kernel.org/bpf/20260720134547.1289964-3-pulehui@huaweicloud.com
>>>
>>>    kernel/bpf/mprog.c | 17 +++++++++++++----
>>>    1 file changed, 13 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/kernel/bpf/mprog.c b/kernel/bpf/mprog.c
>>> index 1394168062e8..af3e6c1c6a1f 100644
>>> --- a/kernel/bpf/mprog.c
>>> +++ b/kernel/bpf/mprog.c
>>> @@ -10,6 +10,8 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
>>>    {
>>>    	struct bpf_link *link = ERR_PTR(-EINVAL);
>>>    	bool id = flags & BPF_F_ID;
>>> +	bool type_mismatch = false;
>>> +	struct bpf_prog *prog;
>>>
>>>    	if (id)
>>>    		link = bpf_link_by_id(id_or_fd);
>>> @@ -17,13 +19,20 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
>>>    		link = bpf_link_get_from_fd(id_or_fd);
>>>    	if (IS_ERR(link))
>>>    		return PTR_ERR(link);
>>> -	if (type && link->prog->type != type) {
>>> +
>>> +	rcu_read_lock();
>>> +	prog = READ_ONCE(link->prog);
>>> +	if (!prog || (type && prog->type != type))
>>> +		type_mismatch = true;
>>> +	rcu_read_unlock();
>>
>> Hm, what about progs under rcu_read_lock_trace cases, wouldn't the UAF
>> still be there?
>>
>> Even though the diff is smaller, I'd kind of lean towards link->type
>> testing since this addresses the underlying issue and avoids touching
>> the prog completely.. do you want me to look into it and also add a
>> BPF selftest to it as patch 2/2?
>>
> 
> I think Pu's implication was that an RCU tasks trace GP would imply RCU gp, thus
> using RCU read lock for protection in both cases would be sufficient for the
> link's free path to wait for this reader. More context is available in [0]. I do

Thanks Kumar for clarifying! I originally used both RCU and Tasks-Trace 
RCU, but Mykyta pointed out that standard RCU alone is sufficient [0].

[0]: 
https://lore.kernel.org/bpf/1289e62e-e956-40c9-a93b-22feefb9110f@gmail.com/

> think this merits a comment for clarity, even in [0] I explicitly commented
> about it everywhere even when it felt unnecessary.

Yeah, I also felt adding a comment was necessary. But haha, Andrii 
thought it might actually cause confusion instead.[1]

[1]: 
https://lore.kernel.org/bpf/CAEf4BzYFQ6wiDwz=3pyvDpr_UcSAOTygMq9pPVsJ=15CaGbA2Q@mail.gmail.com

> 
> I didn't closely follow this set of fixes (those in addition to this) yet but in
> general, we also need to be careful about tracepoint BPF links. Those will only
> wait for SRCU gp, which is not implied for rcu_read_lock() etc., so will need
> their own distinct critical section. The fix pertaining to that was in [1].

This was definitely a blind spot for me. I'll review the previous fixes 
to make sure they're complete. Thank you very much, Kumar!

> 
>    [0]: https://lore.kernel.org/bpf/20260407162234.785270-1-memxor@gmail.com
>    [1]: https://lore.kernel.org/bpf/20260331211021.1632902-2-memxor@gmail.com
> 
>>> +	if (type_mismatch) {
>>>    		bpf_link_put(link);
>>>    		return -EINVAL;
>>>    	}
>>>
>>>    	tuple->link = link;
>>> -	tuple->prog = link->prog;
>>> +	tuple->prog = prog;
>>>    	return 0;
>>>    }
>>>
>>> @@ -343,8 +352,8 @@ int bpf_mprog_detach(struct bpf_mprog_entry *entry,
>>>    	if (!bpf_mprog_total(entry))
>>>    		return -ENOENT;
>>>    	ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags,
>>> -				       prog ? prog->type :
>>> -				       BPF_PROG_TYPE_UNSPEC);
>>> +				       /* Use UNSPEC as wildcard when prog is NULL */
>>> +				       prog ? prog->type : BPF_PROG_TYPE_UNSPEC);
>>>    	if (ret)
>>>    		return ret;
>>>    	if (dtuple.prog) {


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH bpf v4] bpf: Fix UAF when reading prog type in bpf_mprog_link
  2026-08-03 19:00 ` Daniel Borkmann
  2026-08-03 19:14   ` Kumar Kartikeya Dwivedi
@ 2026-08-05  4:18   ` Pu Lehui
  1 sibling, 0 replies; 5+ messages in thread
From: Pu Lehui @ 2026-08-05  4:18 UTC (permalink / raw)
  To: Daniel Borkmann, bpf, linux-kernel
  Cc: Alexei Starovoitov, Andrii Nakryiko, Eduard Zingerman,
	Kumar Kartikeya Dwivedi, Martin KaFai Lau, Yonghong Song,
	Song Liu, Jiri Olsa, Emil Tsalapatis, Pu Lehui


On 2026/8/4 3:00, Daniel Borkmann wrote:
> On 7/28/26 9:42 AM, Pu Lehui wrote:
>> From: Pu Lehui <pulehui@huawei.com>
>>
>> In bpf_mprog_link, the code currently allows a user to pass an abnormal
>> non-netkit or non-tcx link via relative_fd. If a concurrent
>> BPF_LINK_UPDATE is performed on this abnormal link before dereferencing
>> link->prog->type in bpf_mprog_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 = bpf_link_get_from_fd(id_or_fd);
>>                                            BPF_LINK_UPDATE on relative 
>> link
>>                                            ...
>>                                            old_prog = 
>> xchg(&link->link.prog, new_prog);
>>                                            bpf_prog_put(old_prog);
>>    if (type && link->prog->type != 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.
>>
>> To resolve this, we access link->prog under RCU protection to safely
>> fetch the pointer and guarantee its lifetime during the type check.
>> Meanwhile, add a comment explaining that when ptype == 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 <sashiko-bot@kernel.org>
>> Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
>> Signed-off-by: Pu Lehui <pulehui@huawei.com>
>> ---
>> v4:
>> - Access prog->type under rcu protection to simplify the repair logic,
>>    and let unconditional detachment make sense when the bare prog or
>>    link->prog being detached is NULL.
>> - Add Reviewed-by tag by Emil.
>> - Separate from patchset [0]. (Andrii)
>>
>> Link: 
>> https://lore.kernel.org/bpf/f87b53c0-8f00-45a6-82db-8242fa9b143f@huaweicloud.com [0]
>>
>> v3: 
>> https://lore.kernel.org/bpf/20260722072326.1545677-3-pulehui@huaweicloud.com
>> - BPF_F_LINK flag set means the relative id_or_fd is a link, not the 
>> object being
>>    attached. We can not get the link while attach a bare prog with 
>> relative link.
>>    So passing expected link type from callers of 
>> bpf_mprog_attach/detach. (Sashiko)
>> - Add comment to explain that why ptype == UNSPEC. (Emil)
>>
>> v2: 
>> https://lore.kernel.org/bpf/20260721041048.1394085-3-pulehui@huaweicloud.com
>> - Improve commit msg for patch 2. (Amery)
>>
>> v1: 
>> https://lore.kernel.org/bpf/20260720134547.1289964-3-pulehui@huaweicloud.com
>>
>>   kernel/bpf/mprog.c | 17 +++++++++++++----
>>   1 file changed, 13 insertions(+), 4 deletions(-)
>>
>> diff --git a/kernel/bpf/mprog.c b/kernel/bpf/mprog.c
>> index 1394168062e8..af3e6c1c6a1f 100644
>> --- a/kernel/bpf/mprog.c
>> +++ b/kernel/bpf/mprog.c
>> @@ -10,6 +10,8 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
>>   {
>>       struct bpf_link *link = ERR_PTR(-EINVAL);
>>       bool id = flags & BPF_F_ID;
>> +    bool type_mismatch = false;
>> +    struct bpf_prog *prog;
>>       if (id)
>>           link = bpf_link_by_id(id_or_fd);
>> @@ -17,13 +19,20 @@ static int bpf_mprog_link(struct bpf_tuple *tuple,
>>           link = bpf_link_get_from_fd(id_or_fd);
>>       if (IS_ERR(link))
>>           return PTR_ERR(link);
>> -    if (type && link->prog->type != type) {
>> +
>> +    rcu_read_lock();
>> +    prog = READ_ONCE(link->prog);
>> +    if (!prog || (type && prog->type != type))
>> +        type_mismatch = true;
>> +    rcu_read_unlock();
> 
> Hm, what about progs under rcu_read_lock_trace cases, wouldn't the UAF
> still be there?
> 
> Even though the diff is smaller, I'd kind of lean towards link->type
> testing since this addresses the underlying issue and avoids touching
> the prog completely.. do you want me to look into it and also add a
> BPF selftest to it as patch 2/2?

Hi Daniel, that would be awesome! I'd be very happy for you to look into 
it and add the selftest.

FWIW, I actually tried the link->type check earlier [0], but it seemed 
to remove the wildcard semantics during detach and might break the 
interface. Thanks a lot!

[0] 
https://lore.kernel.org/bpf/20260722072326.1545677-3-pulehui@huaweicloud.com

> 
>> +    if (type_mismatch) {
>>           bpf_link_put(link);
>>           return -EINVAL;
>>       }
>>       tuple->link = link;
>> -    tuple->prog = link->prog;
>> +    tuple->prog = prog;
>>       return 0;
>>   }
>> @@ -343,8 +352,8 @@ int bpf_mprog_detach(struct bpf_mprog_entry *entry,
>>       if (!bpf_mprog_total(entry))
>>           return -ENOENT;
>>       ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags,
>> -                       prog ? prog->type :
>> -                       BPF_PROG_TYPE_UNSPEC);
>> +                       /* Use UNSPEC as wildcard when prog is NULL */
>> +                       prog ? prog->type : BPF_PROG_TYPE_UNSPEC);
>>       if (ret)
>>           return ret;
>>       if (dtuple.prog) {


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-08-05  4:19 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-28  7:42 [PATCH bpf v4] bpf: Fix UAF when reading prog type in bpf_mprog_link Pu Lehui
2026-08-03 19:00 ` Daniel Borkmann
2026-08-03 19:14   ` Kumar Kartikeya Dwivedi
2026-08-05  4:11     ` Pu Lehui
2026-08-05  4:18   ` Pu Lehui

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox