public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH bpf-next v2 00/11] Dynptr cleanup and bugfixes
@ 2026-03-07  6:44 Amery Hung
  2026-03-07  6:44 ` [RFC PATCH bpf-next v2 01/11] bpf: Set kfunc dynptr arg type flag based on prototype Amery Hung
                   ` (11 more replies)
  0 siblings, 12 replies; 46+ messages in thread
From: Amery Hung @ 2026-03-07  6:44 UTC (permalink / raw)
  To: bpf
  Cc: netdev, alexei.starovoitov, andrii, daniel, memxor, martin.lau,
	ameryhung, kernel-team

This patchset (1) cleans up dynptr handling (2) refactors object parent-
child relationship tracking to make it more precise and (3) fixes dynptr
UAF bug due to a missing link between dynptr and the parent referenced
object in the verifier.

This patchset will make dynptr tracks its parent object. In bpf qdisc
programs, an skb may be freed through kfuncs. However, since dynptr
currently does not track the parent referenced object (e.g., skb), the
verifier will not invalidate the dynptr after the skb is freed,
resulting in use-after-free. The similar issue also affects file dynptr.
To solve the issue, we need to track the parent skb in the derived
dynptr and slices.

However, we need to refactor the verifier's object tracking mechanism
first because id and ref_obj_id cannot easily express more than simple
object relationship. To illustrate this, we use an example as shown in
the figure below.

Before: object (id,ref_obj_id,dynptr_id)
  id         = id of the object (used for nullness tracking)
  ref_obj_id = id of the underlying referenced object (used for lifetime
               tracking)
  dynptr_id  = id of the parent dynptr of the slice (used for tracking
               parent dynptr, only for PTR_TO_MEM)

                      skb (0,1,0)
                             ^ (try to link dynptr to parent ref_obj_id)
                             +-------------------------------+
                             |           bpf_dynptr_clone    |
                 dynptr A (2,1,0)                dynptr C (4,1,0)
                           ^                               ^
        bpf_dynptr_slice   |                               |
                           |                               |
              slice B (3,1,2)                 slice D (5,1,4)
                         ^
    bpf_dynptr_from_mem  |
    (NOT allowed yet)    |
             dynptr E (6,1,0)

Lets first try to fix the bug by letting dynptr track the parent skb
using ref_obj_id and propagating the ref_obj_id to slices so that when
the skb goes away the derived dynptrs and slices will also be
invalidated. However, if dynptr A is destroyed by overwriting the stack
slot, release_reference(ref_obj_id=1) would be called and all nodes will
be invaldiated. The correct handling should leave skb, dynptr C, and
slice D intact since non-referenced dynptr clone's lifetime does not
need to tie to the original dynptr. This is not a problem before since
dynptr created from skb has ref_obj_id = 0. In the future if we start
allowing creating dynptr from slice, the current design also cannot
correctly handle the removal of dynptr E. All objects will be
incorrectly invalidated instead of only invalidating childrens of
dynptr E. While it is possible to solve the issue by adding more
specialized handling in the dynptr paths [0], it creates more complexity.

To track precise object relationship in a simpler way, u32 parent_id is
added to bpf_reg_state to track parent object. This replaces the
PTR_TO_MEM specific dynptr_id. Therefore, for dynptr A, since it is a
non-referenced dynptr, its ref_obj_id is set to 0. The parent_id will be
set to 1 to track the id of the skb. Note that, this will not grow
bpf_reg_state on 64 byte machine as there is a 7-byte padding.

After: object (id,ref_obj_id,parent_id)
  id         = id of the object (used for nullness tracking)
  ref_obj_id = id of the referenced object; objects with same ref_obj_id
               have the same lifetime (used for lifetime tracking)
  parent_id  = id of the parent object; points to id (used for object
               relationship tracking)

(1) Non-referenced dynptr with referenced parent (e.g., skb in Qdisc):

                          skb (1,1,0)
                               ^
          bpf_dynptr_from_skb  +-------------------------------+
                               |      bpf_dynptr_clone(A, C)   |
                 dynptr A (2,0,1)               dynptr C (4,0,1)
                           ^                              ^
        bpf_dynptr_slice   |                              |
                           |                              |
              slice B (3,0,2)                slice D (5,0,4)
                       ^
  bpf_dynptr_from_mem  |
  (NOT allowed yet)    |
         dynptr E (6,0,3)

The figures below show how the new design works in different 
referenced/non-referenced dynptr + referenced/non-referenced parent
combinations. The relationship between slices and dynptrs is ignored
as they are still the same. The main difference is how clone dynptrs
are represented. Since bpf_dynptr_clone() does not initializes a new
dynptr, the clone of referenced dynptr cannot function when the
original or any of the clone is invalidated. To represent this,
they will share the same ref_obj_id. For non-referenced dynptr, the
original and the clones will be able live independently.


(2) Non-referenced dynptr with non-referenced parent (e.g., skb in TC,
    always valid):

      bpf_dynptr_from_skb
                                  bpf_dynptr_clone(A, C)
             dynptr A (1,0,0)                  dynptr C (2,0,0)

                         dynptr A and C live independently

(3) Referenced dynptr with referenced parent:

                     file (1,1,0)
                           ^ ^
     bpf_dynptr_from_file  | +-------------------------------+
                           |       bpf_dynptr_clone(A, C)    |
             dynptr A (2,3,1)                  dynptr C (4,3,1)
                         ^                                 ^
                         |                                 |
                         dynptr A and C have the same lifetime


(4) Referenced dynptr with non-referenced parent:

 bpf_ringbuf_reserve_dynptr  
                                  bpf_dynptr_clone(A, C)
             dynptr A (1,1,0)                  dynptr C (2,1,0)
                         ^                                 ^
                         |                                 |
                         dynptr A and C have the same lifetime


I also tried folding id and ref_obj_id into id and using ref_obj_id to
track parent [1]. This design was not able to express the relationship
of referenced sk pointer and casted referenced sk pointer. The two
objects needs two id to express that they have the same lifetime but
different nullness.

Referenced socket pointer:

                                C = ptr_casting_function(A)
                ptr A (1,1,0)                     ptr C (2,1,0)
                         ^                                 ^
                         |                                 |
                        ptr C may be NULL even if ptr A is valid
			but they have the same lifetime


To avoid recursive call chain of release_reference() ->
unmark_stack_slots_dynptr(), release_reference() now uses
stacked-based DFS to find and invalidate registers and stack slots
containing the to-be-released id/ref_obj_id and all dependant ids
whose parent_id matches the id. Currently, it skips id == 0, which
however maybe a valid id e.g., pkt pointer by reading ctx. Future work
may start giving them > 0 id. This should not affect the current usecase
where skb and file are both given > 0 id.

[0] https://lore.kernel.org/bpf/20250414161443.1146103-2-memxor@gmail.com/
[1] https://github.com/ameryhung/bpf/commits/obj_relationship_v2_no_parent_id/ 


Changelog:

v1 -> v2
  - Redesign: Use object (id, ref_obj_id, parent_id) instead of 
    (id, ref_obj_id) as it cannot express ptr casting without
    introduing specialized code to handle the case
  - Use stack-based DFS to release objects to avoid recursion (Andrii)
  - Keep reg->id after null check
  - Add dynptr cleanup
  - Fix dynptr kfunc arg type determination
  - Add a file dynptr UAF selftest
  Link: https://lore.kernel.org/bpf/20260202214817.2853236-1-ameryhung@gmail.com/

---

Amery Hung (11):
  bpf: Set kfunc dynptr arg type flag based on prototype
  selftests/bpf: Test passing CONST_PTR_TO_DYNPTR to kfunc that may
    mutate dynptr
  bpf: Unify dynptr handling in the verifier
  bpf: Assign reg->id when getting referenced kptr from ctx
  bpf: Preserve reg->id of pointer objects after null-check
  bpf: Refactor object relationship tracking and fix dynptr UAF bug
  bpf: Remove redundant dynptr arg check for helper
  selftests/bpf: Test creating dynptr from dynptr data and slice
  selftests/bpf: Test using dynptr after freeing the underlying object
  selftests/bpf: Test using slice after invalidating dynptr clone
  selftests/bpf: Test using file dynptr after the reference on file is
    dropped

 fs/verity/measure.c                           |   2 +-
 include/linux/bpf.h                           |   8 +-
 include/linux/bpf_verifier.h                  |  14 +-
 kernel/bpf/helpers.c                          |  10 +-
 kernel/bpf/log.c                              |   4 +-
 kernel/bpf/verifier.c                         | 496 +++++++-----------
 kernel/trace/bpf_trace.c                      |  18 +-
 tools/testing/selftests/bpf/bpf_kfuncs.h      |   6 +-
 .../selftests/bpf/prog_tests/bpf_qdisc.c      |  50 ++
 .../bpf/progs/bpf_qdisc_dynptr_clone.c        |  69 +++
 .../progs/bpf_qdisc_fail__invalid_dynptr.c    |  62 +++
 ...f_qdisc_fail__invalid_dynptr_cross_frame.c |  68 +++
 .../bpf_qdisc_fail__invalid_dynptr_slice.c    |  64 +++
 .../testing/selftests/bpf/progs/dynptr_fail.c |  85 ++-
 .../selftests/bpf/progs/dynptr_success.c      |   6 +-
 .../selftests/bpf/progs/file_reader_fail.c    |  60 +++
 .../bpf/progs/test_kfunc_dynptr_param.c       |   9 +-
 .../selftests/bpf/progs/user_ringbuf_fail.c   |   4 +-
 18 files changed, 684 insertions(+), 351 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_dynptr_clone.c
 create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr.c
 create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr_cross_frame.c
 create mode 100644 tools/testing/selftests/bpf/progs/bpf_qdisc_fail__invalid_dynptr_slice.c

-- 
2.47.3


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

end of thread, other threads:[~2026-03-17 18:49 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-07  6:44 [RFC PATCH bpf-next v2 00/11] Dynptr cleanup and bugfixes Amery Hung
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 01/11] bpf: Set kfunc dynptr arg type flag based on prototype Amery Hung
2026-03-11 14:47   ` Mykyta Yatsenko
2026-03-11 16:34     ` Amery Hung
2026-03-11 19:43   ` Andrii Nakryiko
2026-03-11 20:01     ` Amery Hung
2026-03-11 22:37       ` Andrii Nakryiko
2026-03-11 23:03         ` Amery Hung
2026-03-11 23:15           ` Andrii Nakryiko
2026-03-12 16:59             ` Amery Hung
2026-03-12 20:09               ` Andrii Nakryiko
2026-03-13  3:25                 ` Alexei Starovoitov
2026-03-16 20:57   ` Eduard Zingerman
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 02/11] selftests/bpf: Test passing CONST_PTR_TO_DYNPTR to kfunc that may mutate dynptr Amery Hung
2026-03-11 15:26   ` Mykyta Yatsenko
2026-03-11 16:38     ` Amery Hung
2026-03-11 16:56       ` Amery Hung
2026-03-16 21:35   ` Eduard Zingerman
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 03/11] bpf: Unify dynptr handling in the verifier Amery Hung
2026-03-11 16:03   ` Mykyta Yatsenko
2026-03-11 17:23     ` Amery Hung
2026-03-11 22:22       ` Mykyta Yatsenko
2026-03-11 22:35         ` Amery Hung
2026-03-11 19:57   ` Andrii Nakryiko
2026-03-11 20:16     ` Amery Hung
2026-03-16 22:52   ` Eduard Zingerman
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 04/11] bpf: Assign reg->id when getting referenced kptr from ctx Amery Hung
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 05/11] bpf: Preserve reg->id of pointer objects after null-check Amery Hung
2026-03-11 21:55   ` Andrii Nakryiko
2026-03-11 22:26   ` Alexei Starovoitov
2026-03-11 22:29     ` Alexei Starovoitov
2026-03-11 23:46       ` Amery Hung
2026-03-17 18:49         ` Eduard Zingerman
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 06/11] bpf: Refactor object relationship tracking and fix dynptr UAF bug Amery Hung
2026-03-11 22:32   ` Andrii Nakryiko
2026-03-13 20:32     ` Amery Hung
2026-03-12 23:33   ` Mykyta Yatsenko
2026-03-13 20:33     ` Amery Hung
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 07/11] bpf: Remove redundant dynptr arg check for helper Amery Hung
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 08/11] selftests/bpf: Test creating dynptr from dynptr data and slice Amery Hung
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 09/11] selftests/bpf: Test using dynptr after freeing the underlying object Amery Hung
2026-03-16 19:25   ` Eduard Zingerman
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 10/11] selftests/bpf: Test using slice after invalidating dynptr clone Amery Hung
2026-03-07  6:44 ` [RFC PATCH bpf-next v2 11/11] selftests/bpf: Test using file dynptr after the reference on file is dropped Amery Hung
2026-03-11 19:38 ` [RFC PATCH bpf-next v2 00/11] Dynptr cleanup and bugfixes Andrii Nakryiko
2026-03-13 20:49   ` Amery Hung

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