€•P¯Œsphinx.addnodes”Œdocument”“”)�”}”(Œ rawsource”Œ”Œchildren”]”(Œ translations”Œ LanguagesNode”“”)�”}”(hhh]”(hŒ pending_xref”“”)�”}”(hhh]”Œdocutils.nodes”ŒText”“”ŒChinese (Simplified)”…”�”}”Œparent”hsbaŒ attributes”}”(Œids”]”Œclasses”]”Œnames”]”Œdupnames”]”Œbackrefs”]”Œ refdomain”Œstd”Œreftype”Œdoc”Œ reftarget”Œ%/translations/zh_CN/bpf/graph_ds_impl”Œmodname”NŒ classname”NŒ refexplicit”ˆuŒtagname”hhh ubh)�”}”(hhh]”hŒChinese (Traditional)”…”�”}”hh2sbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ%/translations/zh_TW/bpf/graph_ds_impl”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)�”}”(hhh]”hŒItalian”…”�”}”hhFsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ%/translations/it_IT/bpf/graph_ds_impl”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)�”}”(hhh]”hŒJapanese”…”�”}”hhZsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ%/translations/ja_JP/bpf/graph_ds_impl”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)�”}”(hhh]”hŒKorean”…”�”}”hhnsbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ%/translations/ko_KR/bpf/graph_ds_impl”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)�”}”(hhh]”hŒPortuguese (Brazilian)”…”�”}”hh‚sbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ%/translations/pt_BR/bpf/graph_ds_impl”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubh)�”}”(hhh]”hŒSpanish”…”�”}”hh–sbah}”(h]”h ]”h"]”h$]”h&]”Œ refdomain”h)Œreftype”h+Œ reftarget”Œ%/translations/sp_SP/bpf/graph_ds_impl”Œmodname”NŒ classname”NŒ refexplicit”ˆuh1hhh ubeh}”(h]”h ]”h"]”h$]”h&]”Œcurrent_language”ŒEnglish”uh1h hhŒ _document”hŒsource”NŒline”NubhŒsection”“”)�”}”(hhh]”(hŒtitle”“”)�”}”(hŒBPF Graph Data Structures”h]”hŒBPF Graph Data Structures”…”�”}”(hh¼h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hºhh·h²hh³Œ?/var/lib/git/docbuild/linux/Documentation/bpf/graph_ds_impl.rst”h´KubhŒ paragraph”“”)�”}”(hŒÑThis document describes implementation details of new-style "graph" data structures (linked_list, rbtree), with particular focus on the verifier's implementation of semantics specific to those data structures.”h]”hŒ×This document describes implementation details of new-style “graphâ€� data structures (linked_list, rbtree), with particular focus on the verifier’s implementation of semantics specific to those data structures.”…”�”}”(hhÍh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Khh·h²hubhÌ)�”}”(hŒ¼Although no specific verifier code is referred to in this document, the document assumes that the reader has general knowledge of BPF verifier internals, BPF maps, and BPF program writing.”h]”hŒ¼Although no specific verifier code is referred to in this document, the document assumes that the reader has general knowledge of BPF verifier internals, BPF maps, and BPF program writing.”…”�”}”(hhÛh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K hh·h²hubhÌ)�”}”(hŒ¼Note that the intent of this document is to describe the current state of these graph data structures. **No guarantees** of stability for either semantics or APIs are made or implied here.”h]”(hŒgNote that the intent of this document is to describe the current state of these graph data structures. ”…”�”}”(hhéh²hh³Nh´NubhŒstrong”“”)�”}”(hŒ**No guarantees**”h]”hŒ No guarantees”…”�”}”(hhóh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hñhhéubhŒD of stability for either semantics or APIs are made or implied here.”…”�”}”(hhéh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K hh·h²hubhŒtopic”“”)�”}”(hhh]”hŒ bullet_list”“”)�”}”(hhh]”(hŒ list_item”“”)�”}”(hhh]”hÌ)�”}”(hhh]”hŒ reference”“”)�”}”(hhh]”hŒ Introduction”…”�”}”(hjh²hh³Nh´Nubah}”(h]”Œid1”ah ]”h"]”h$]”h&]”Œrefid”Œ introduction”uh1jhjubah}”(h]”h ]”h"]”h$]”h&]”uh1hËhjubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubj)�”}”(hhh]”hÌ)�”}”(hhh]”j)�”}”(hhh]”hŒ Unstable API”…”�”}”(hjAh²hh³Nh´Nubah}”(h]”Œid2”ah ]”h"]”h$]”h&]”Œrefid”Œ unstable-api”uh1jhj>ubah}”(h]”h ]”h"]”h$]”h&]”uh1hËhj;ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubj)�”}”(hhh]”hÌ)�”}”(hhh]”j)�”}”(hhh]”hŒLocking”…”�”}”(hjch²hh³Nh´Nubah}”(h]”Œid3”ah ]”h"]”h$]”h&]”Œrefid”Œlocking”uh1jhj`ubah}”(h]”h ]”h"]”h$]”h&]”uh1hËhj]ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubj)�”}”(hhh]”hÌ)�”}”(hhh]”j)�”}”(hhh]”hŒNon-owning references”…”�”}”(hj…h²hh³Nh´Nubah}”(h]”Œid4”ah ]”h"]”h$]”h&]”Œrefid”Œnon-owning-references”uh1jhj‚ubah}”(h]”h ]”h"]”h$]”h&]”uh1hËhjubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubeh}”(h]”h ]”h"]”h$]”h&]”uh1jhj h²hh³Nh´Nubah}”(h]”Œcontents”ah ]”(Œcontents”Œlocal”eh"]”Œcontents”ah$]”h&]”uh1j h³hÊh´Khh·h²hubh¶)�”}”(hhh]”(h»)�”}”(hŒ Introduction”h]”hŒ Introduction”…”�”}”(hj´h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”Œrefid”j(uh1hºhj±h²hh³hÊh´KubhÌ)�”}”(hXfThe BPF map API has historically been the main way to expose data structures of various types for use within BPF programs. Some data structures fit naturally with the map API (HASH, ARRAY), others less so. Consequently, programs interacting with the latter group of data structures can be hard to parse for kernel programmers without previous BPF experience.”h]”hXfThe BPF map API has historically been the main way to expose data structures of various types for use within BPF programs. Some data structures fit naturally with the map API (HASH, ARRAY), others less so. Consequently, programs interacting with the latter group of data structures can be hard to parse for kernel programmers without previous BPF experience.”…”�”}”(hjÃh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Khj±h²hubhÌ)�”}”(hX5Luckily, some restrictions which necessitated the use of BPF map semantics are no longer relevant. With the introduction of kfuncs, kptrs, and the any-context BPF allocator, it is now possible to implement BPF data structures whose API and semantics more closely match those exposed to the rest of the kernel.”h]”hX5Luckily, some restrictions which necessitated the use of BPF map semantics are no longer relevant. With the introduction of kfuncs, kptrs, and the any-context BPF allocator, it is now possible to implement BPF data structures whose API and semantics more closely match those exposed to the rest of the kernel.”…”�”}”(hjÑh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Khj±h²hubhÌ)�”}”(hXTwo such data structures - linked_list and rbtree - have many verification details in common. Because both have "root"s ("head" for linked_list) and "node"s, the verifier code and this document refer to common functionality as "graph_api", "graph_root", "graph_node", etc.”h]”hX(Two such data structures - linked_list and rbtree - have many verification details in common. Because both have “rootâ€�s (“headâ€� for linked_list) and “nodeâ€�s, the verifier code and this document refer to common functionality as “graph_apiâ€�, “graph_rootâ€�, “graph_nodeâ€�, etc.”…”�”}”(hjßh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K#hj±h²hubhÌ)�”}”(hŒZUnless otherwise stated, examples and semantics below apply to both graph data structures.”h]”hŒZUnless otherwise stated, examples and semantics below apply to both graph data structures.”…”�”}”(hjíh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K(hj±h²hubeh}”(h]”j.ah ]”h"]”Œ introduction”ah$]”h&]”uh1hµhh·h²hh³hÊh´Kubh¶)�”}”(hhh]”(h»)�”}”(hŒ Unstable API”h]”hŒ Unstable API”…”�”}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”jÂjJuh1hºhjh²hh³hÊh´K,ubhÌ)�”}”(hXÂData structures implemented using the BPF map API have historically used BPF helper functions - either standard map API helpers like ``bpf_map_update_elem`` or map-specific helpers. The new-style graph data structures instead use kfuncs to define their manipulation helpers. Because there are no stability guarantees for kfuncs, the API and semantics for these data structures can be evolved in a way that breaks backwards compatibility if necessary.”h]”(hŒ…Data structures implemented using the BPF map API have historically used BPF helper functions - either standard map API helpers like ”…”�”}”(hjh²hh³Nh´NubhŒliteral”“”)�”}”(hŒ``bpf_map_update_elem``”h]”hŒbpf_map_update_elem”…”�”}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhX& or map-specific helpers. The new-style graph data structures instead use kfuncs to define their manipulation helpers. Because there are no stability guarantees for kfuncs, the API and semantics for these data structures can be evolved in a way that breaks backwards compatibility if necessary.”…”�”}”(hjh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K.hjh²hubhÌ)�”}”(hŒhRoot and node types for the new data structures are opaquely defined in the ``uapi/linux/bpf.h`` header.”h]”(hŒLRoot and node types for the new data structures are opaquely defined in the ”…”�”}”(hj5h²hh³Nh´Nubj)�”}”(hŒ``uapi/linux/bpf.h``”h]”hŒuapi/linux/bpf.h”…”�”}”(hj=h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj5ubhŒ header.”…”�”}”(hj5h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K5hjh²hubeh}”(h]”jPah ]”h"]”Œ unstable api”ah$]”h&]”uh1hµhh·h²hh³hÊh´K,ubh¶)�”}”(hhh]”(h»)�”}”(hŒLocking”h]”hŒLocking”…”�”}”(hj_h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”jÂjluh1hºhj\h²hh³hÊh´K9ubhÌ)�”}”(hŒkThe new-style data structures are intrusive and are defined similarly to their vanilla kernel counterparts:”h]”hŒkThe new-style data structures are intrusive and are defined similarly to their vanilla kernel counterparts:”…”�”}”(hjmh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K;hj\h²hubhŒ literal_block”“”)�”}”(hŒœstruct node_data { long key; long data; struct bpf_rb_node node; }; struct bpf_spin_lock glock; struct bpf_rb_root groot __contains(node_data, node);”h]”hŒœstruct node_data { long key; long data; struct bpf_rb_node node; }; struct bpf_spin_lock glock; struct bpf_rb_root groot __contains(node_data, node);”…”�”}”hj}sbah}”(h]”h ]”h"]”h$]”h&]”Œ xml:space”Œpreserve”Œforce”‰Œlanguage”Œc”Œhighlight_args”}”uh1j{h³hÊh´K>hj\h²hubhÌ)�”}”(hXÿThe "root" type for both linked_list and rbtree expects to be in a map_value which also contains a ``bpf_spin_lock`` - in the above example both global variables are placed in a single-value arraymap. The verifier considers this spin_lock to be associated with the ``bpf_rb_root`` by virtue of both being in the same map_value and will enforce that the correct lock is held when verifying BPF programs that manipulate the tree. Since this lock checking happens at verification time, there is no runtime penalty.”h]”(hŒgThe “rootâ€� type for both linked_list and rbtree expects to be in a map_value which also contains a ”…”�”}”(hj’h²hh³Nh´Nubj)�”}”(hŒ``bpf_spin_lock``”h]”hŒ bpf_spin_lock”…”�”}”(hjšh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj’ubhŒ• - in the above example both global variables are placed in a single-value arraymap. The verifier considers this spin_lock to be associated with the ”…”�”}”(hj’h²hh³Nh´Nubj)�”}”(hŒ``bpf_rb_root``”h]”hŒ bpf_rb_root”…”�”}”(hj¬h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj’ubhŒç by virtue of both being in the same map_value and will enforce that the correct lock is held when verifying BPF programs that manipulate the tree. Since this lock checking happens at verification time, there is no runtime penalty.”…”�”}”(hj’h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KIhj\h²hubeh}”(h]”jrah ]”h"]”Œlocking”ah$]”h&]”uh1hµhh·h²hh³hÊh´K9ubh¶)�”}”(hhh]”(h»)�”}”(hŒNon-owning references”h]”hŒNon-owning references”…”�”}”(hjÎh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”jÂjŽuh1hºhjËh²hh³hÊh´KRubhÌ)�”}”(hŒ**Motivation**”h]”hò)�”}”(hjÞh]”hŒ Motivation”…”�”}”(hjàh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hñhjÜubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KThjËh²hubhÌ)�”}”(hŒ Consider the following BPF code:”h]”hŒ Consider the following BPF code:”…”�”}”(hjóh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KVhjËh²hubj|)�”}”(hŒ•struct node_data *n = bpf_obj_new(typeof(*n)); /* ACQUIRED */ bpf_spin_lock(&lock); bpf_rbtree_add(&tree, n); /* PASSED */ bpf_spin_unlock(&lock);”h]”hŒ•struct node_data *n = bpf_obj_new(typeof(*n)); /* ACQUIRED */ bpf_spin_lock(&lock); bpf_rbtree_add(&tree, n); /* PASSED */ bpf_spin_unlock(&lock);”…”�”}”hjsbah}”(h]”h ]”h"]”h$]”h&]”j‹jŒj�‰jŽj�j�}”uh1j{h³hÊh´KXhjËh²hubhÌ)�”}”(hXÒFrom the verifier's perspective, the pointer ``n`` returned from ``bpf_obj_new`` has type ``PTR_TO_BTF_ID | MEM_ALLOC``, with a ``btf_id`` of ``struct node_data`` and a nonzero ``ref_obj_id``. Because it holds ``n``, the program has ownership of the pointee's (object pointed to by ``n``) lifetime. The BPF program must pass off ownership before exiting - either via ``bpf_obj_drop``, which ``free``'s the object, or by adding it to ``tree`` with ``bpf_rbtree_add``.”h]”(hŒ/From the verifier’s perspective, the pointer ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``n``”h]”hŒn”…”�”}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ returned from ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_new``”h]”hŒ bpf_obj_new”…”�”}”(hj*h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ has type ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``PTR_TO_BTF_ID | MEM_ALLOC``”h]”hŒPTR_TO_BTF_ID | MEM_ALLOC”…”�”}”(hj<h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ , with a ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ ``btf_id``”h]”hŒbtf_id”…”�”}”(hjNh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ of ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``struct node_data``”h]”hŒstruct node_data”…”�”}”(hj`h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ and a nonzero ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``ref_obj_id``”h]”hŒ ref_obj_id”…”�”}”(hjrh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ. Because it holds ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``n``”h]”hŒn”…”�”}”(hj„h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒE, the program has ownership of the pointee’s (object pointed to by ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``n``”h]”hŒn”…”�”}”(hj–h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒP) lifetime. The BPF program must pass off ownership before exiting - either via ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_drop``”h]”hŒ bpf_obj_drop”…”�”}”(hj¨h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ, which ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``free``”h]”hŒfree”…”�”}”(hjºh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ$’s the object, or by adding it to ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``tree``”h]”hŒtree”…”�”}”(hjÌh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ with ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``bpf_rbtree_add``”h]”hŒbpf_rbtree_add”…”�”}”(hjÞh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ.”…”�”}”(hjh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KbhjËh²hubhÌ)�”}”(hŒ�(``ACQUIRED`` and ``PASSED`` comments in the example denote statements where "ownership is acquired" and "ownership is passed", respectively)”h]”(hŒ(”…”�”}”(hjöh²hh³Nh´Nubj)�”}”(hŒ ``ACQUIRED``”h]”hŒACQUIRED”…”�”}”(hjþh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjöubhŒ and ”…”�”}”(hjöh²hh³Nh´Nubj)�”}”(hŒ ``PASSED``”h]”hŒPASSED”…”�”}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjöubhŒy comments in the example denote statements where “ownership is acquiredâ€� and “ownership is passedâ€�, respectively)”…”�”}”(hjöh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KjhjËh²hubhÌ)�”}”(hX_What should the verifier do with ``n`` after ownership is passed off? If the object was ``free``'d with ``bpf_obj_drop`` the answer is obvious: the verifier should reject programs which attempt to access ``n`` after ``bpf_obj_drop`` as the object is no longer valid. The underlying memory may have been reused for some other allocation, unmapped, etc.”h]”(hŒ!What should the verifier do with ”…”�”}”(hj(h²hh³Nh´Nubj)�”}”(hŒ``n``”h]”hŒn”…”�”}”(hj0h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj(ubhŒ2 after ownership is passed off? If the object was ”…”�”}”(hj(h²hh³Nh´Nubj)�”}”(hŒ``free``”h]”hŒfree”…”�”}”(hjBh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj(ubhŒ ’d with ”…”�”}”(hj(h²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_drop``”h]”hŒ bpf_obj_drop”…”�”}”(hjTh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj(ubhŒT the answer is obvious: the verifier should reject programs which attempt to access ”…”�”}”(hj(h²hh³Nh´Nubj)�”}”(hŒ``n``”h]”hŒn”…”�”}”(hjfh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj(ubhŒ after ”…”�”}”(hj(h²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_drop``”h]”hŒ bpf_obj_drop”…”�”}”(hjxh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj(ubhŒw as the object is no longer valid. The underlying memory may have been reused for some other allocation, unmapped, etc.”…”�”}”(hj(h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KmhjËh²hubhÌ)�”}”(hŒúWhen ownership is passed to ``tree`` via ``bpf_rbtree_add`` the answer is less obvious. The verifier could enforce the same semantics as for ``bpf_obj_drop``, but that would result in programs with useful, common coding patterns being rejected, e.g.:”h]”(hŒWhen ownership is passed to ”…”�”}”(hj�h²hh³Nh´Nubj)�”}”(hŒ``tree``”h]”hŒtree”…”�”}”(hj˜h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj�ubhŒ via ”…”�”}”(hj�h²hh³Nh´Nubj)�”}”(hŒ``bpf_rbtree_add``”h]”hŒbpf_rbtree_add”…”�”}”(hjªh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj�ubhŒR the answer is less obvious. The verifier could enforce the same semantics as for ”…”�”}”(hj�h²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_drop``”h]”hŒ bpf_obj_drop”…”�”}”(hj¼h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj�ubhŒ], but that would result in programs with useful, common coding patterns being rejected, e.g.:”…”�”}”(hj�h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KshjËh²hubj|)�”}”(hŒ·int x; struct node_data *n = bpf_obj_new(typeof(*n)); /* ACQUIRED */ bpf_spin_lock(&lock); bpf_rbtree_add(&tree, n); /* PASSED */ x = n->data; n->data = 42; bpf_spin_unlock(&lock);”h]”hŒ·int x; struct node_data *n = bpf_obj_new(typeof(*n)); /* ACQUIRED */ bpf_spin_lock(&lock); bpf_rbtree_add(&tree, n); /* PASSED */ x = n->data; n->data = 42; bpf_spin_unlock(&lock);”…”�”}”hjÔsbah}”(h]”h ]”h"]”h$]”h&]”j‹jŒj�‰jŽj�j�}”uh1j{h³hÊh´KxhjËh²hubhÌ)�”}”(hŒ†Both the read from and write to ``n->data`` would be rejected. The verifier can do better, though, by taking advantage of two details:”h]”(hŒ Both the read from and write to ”…”�”}”(hjãh²hh³Nh´Nubj)�”}”(hŒ ``n->data``”h]”hŒn->data”…”�”}”(hjëh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjãubhŒ[ would be rejected. The verifier can do better, though, by taking advantage of two details:”…”�”}”(hjãh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K…hjËh²hubhŒ block_quote”“”)�”}”(hX§* Graph data structure APIs can only be used when the ``bpf_spin_lock`` associated with the graph root is held * Both graph data structures have pointer stability * Because graph nodes are allocated with ``bpf_obj_new`` and adding / removing from the root involves fiddling with the ``bpf_{list,rb}_node`` field of the node struct, a graph node will remain at the same address after either operation. ”h]”j)�”}”(hhh]”(j)�”}”(hŒmGraph data structure APIs can only be used when the ``bpf_spin_lock`` associated with the graph root is held ”h]”hÌ)�”}”(hŒlGraph data structure APIs can only be used when the ``bpf_spin_lock`` associated with the graph root is held”h]”(hŒ4Graph data structure APIs can only be used when the ”…”�”}”(hjh²hh³Nh´Nubj)�”}”(hŒ``bpf_spin_lock``”h]”hŒ bpf_spin_lock”…”�”}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubhŒ' associated with the graph root is held”…”�”}”(hjh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kˆhj ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj ubj)�”}”(hX+Both graph data structures have pointer stability * Because graph nodes are allocated with ``bpf_obj_new`` and adding / removing from the root involves fiddling with the ``bpf_{list,rb}_node`` field of the node struct, a graph node will remain at the same address after either operation. ”h]”(hÌ)�”}”(hŒ1Both graph data structures have pointer stability”h]”hŒ1Both graph data structures have pointer stability”…”�”}”(hj:h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K‹hj6ubj)�”}”(hŒô* Because graph nodes are allocated with ``bpf_obj_new`` and adding / removing from the root involves fiddling with the ``bpf_{list,rb}_node`` field of the node struct, a graph node will remain at the same address after either operation. ”h]”j)�”}”(hhh]”j)�”}”(hŒìBecause graph nodes are allocated with ``bpf_obj_new`` and adding / removing from the root involves fiddling with the ``bpf_{list,rb}_node`` field of the node struct, a graph node will remain at the same address after either operation. ”h]”hÌ)�”}”(hŒëBecause graph nodes are allocated with ``bpf_obj_new`` and adding / removing from the root involves fiddling with the ``bpf_{list,rb}_node`` field of the node struct, a graph node will remain at the same address after either operation.”h]”(hŒ'Because graph nodes are allocated with ”…”�”}”(hjSh²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_new``”h]”hŒ bpf_obj_new”…”�”}”(hj[h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjSubhŒ@ and adding / removing from the root involves fiddling with the ”…”�”}”(hjSh²hh³Nh´Nubj)�”}”(hŒ``bpf_{list,rb}_node``”h]”hŒbpf_{list,rb}_node”…”�”}”(hjmh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjSubhŒ_ field of the node struct, a graph node will remain at the same address after either operation.”…”�”}”(hjSh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K�hjOubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjLubah}”(h]”h ]”h"]”h$]”h&]”Œbullet”Œ*”uh1jh³hÊh´K�hjHubah}”(h]”h ]”h"]”h$]”h&]”uh1jh³hÊh´K�hj6ubeh}”(h]”h ]”h"]”h$]”h&]”uh1jhj ubeh}”(h]”h ]”h"]”h$]”h&]”j‘j’uh1jh³hÊh´Kˆhjubah}”(h]”h ]”h"]”h$]”h&]”uh1jh³hÊh´KˆhjËh²hubhÌ)�”}”(hX”Because the associated ``bpf_spin_lock`` must be held by any program adding or removing, if we're in the critical section bounded by that lock, we know that no other program can add or remove until the end of the critical section. This combined with pointer stability means that, until the critical section ends, we can safely access the graph node through ``n`` even after it was used to pass ownership.”h]”(hŒBecause the associated ”…”�”}”(hj«h²hh³Nh´Nubj)�”}”(hŒ``bpf_spin_lock``”h]”hŒ bpf_spin_lock”…”�”}”(hj³h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj«ubhX? must be held by any program adding or removing, if we’re in the critical section bounded by that lock, we know that no other program can add or remove until the end of the critical section. This combined with pointer stability means that, until the critical section ends, we can safely access the graph node through ”…”�”}”(hj«h²hh³Nh´Nubj)�”}”(hŒ``n``”h]”hŒn”…”�”}”(hjÅh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj«ubhŒ* even after it was used to pass ownership.”…”�”}”(hj«h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K’hjËh²hubhÌ)�”}”(hŒäThe verifier considers such a reference a *non-owning reference*. The ref returned by ``bpf_obj_new`` is accordingly considered an *owning reference*. Both terms currently only have meaning in the context of graph nodes and API.”h]”(hŒ*The verifier considers such a reference a ”…”�”}”(hjÝh²hh³Nh´NubhŒemphasis”“”)�”}”(hŒ*non-owning reference*”h]”hŒnon-owning reference”…”�”}”(hjçh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jåhjÝubhŒ. The ref returned by ”…”�”}”(hjÝh²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_new``”h]”hŒ bpf_obj_new”…”�”}”(hjùh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjÝubhŒ is accordingly considered an ”…”�”}”(hjÝh²hh³Nh´Nubjæ)�”}”(hŒ*owning reference*”h]”hŒowning reference”…”�”}”(hj h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jåhjÝubhŒO. Both terms currently only have meaning in the context of graph nodes and API.”…”�”}”(hjÝh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K™hjËh²hubhÌ)�”}”(hŒ **Details**”h]”hò)�”}”(hj%h]”hŒDetails”…”�”}”(hj'h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hñhj#ubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K�hjËh²hubhÌ)�”}”(hŒ;Let's enumerate the properties of both types of references.”h]”hŒ=Let’s enumerate the properties of both types of references.”…”�”}”(hj:h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KŸhjËh²hubhÌ)�”}”(hŒ*owning reference*”h]”jæ)�”}”(hjJh]”hŒowning reference”…”�”}”(hjLh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jåhjHubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K¡hjËh²hubj)�”}”(hXC* This reference controls the lifetime of the pointee * Ownership of pointee must be 'released' by passing it to some graph API kfunc, or via ``bpf_obj_drop``, which ``free``'s the pointee * If not released before program ends, verifier considers program invalid * Access to the pointee's memory will not page fault ”h]”j)�”}”(hhh]”(j)�”}”(hŒ4This reference controls the lifetime of the pointee ”h]”hÌ)�”}”(hŒ3This reference controls the lifetime of the pointee”h]”hŒ3This reference controls the lifetime of the pointee”…”�”}”(hjjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K£hjfubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjcubj)�”}”(hŒÐOwnership of pointee must be 'released' by passing it to some graph API kfunc, or via ``bpf_obj_drop``, which ``free``'s the pointee * If not released before program ends, verifier considers program invalid ”h]”(hÌ)�”}”(hŒ„Ownership of pointee must be 'released' by passing it to some graph API kfunc, or via ``bpf_obj_drop``, which ``free``'s the pointee”h]”(hŒZOwnership of pointee must be ‘released’ by passing it to some graph API kfunc, or via ”…”�”}”(hj‚h²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_drop``”h]”hŒ bpf_obj_drop”…”�”}”(hjŠh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj‚ubhŒ, which ”…”�”}”(hj‚h²hh³Nh´Nubj)�”}”(hŒ``free``”h]”hŒfree”…”�”}”(hjœh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj‚ubhŒ’s the pointee”…”�”}”(hj‚h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K¥hj~ubj)�”}”(hhh]”j)�”}”(hŒHIf not released before program ends, verifier considers program invalid ”h]”hÌ)�”}”(hŒGIf not released before program ends, verifier considers program invalid”h]”hŒGIf not released before program ends, verifier considers program invalid”…”�”}”(hj»h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K¨hj·ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj´ubah}”(h]”h ]”h"]”h$]”h&]”j‘j’uh1jh³hÊh´K¨hj~ubeh}”(h]”h ]”h"]”h$]”h&]”uh1jhjcubj)�”}”(hŒ3Access to the pointee's memory will not page fault ”h]”hÌ)�”}”(hŒ2Access to the pointee's memory will not page fault”h]”hŒ4Access to the pointee’s memory will not page fault”…”�”}”(hjßh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KªhjÛubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjcubeh}”(h]”h ]”h"]”h$]”h&]”j‘j’uh1jh³hÊh´K£hj_ubah}”(h]”h ]”h"]”h$]”h&]”uh1jh³hÊh´K£hjËh²hubhÌ)�”}”(hŒ*non-owning reference*”h]”jæ)�”}”(hjh]”hŒnon-owning reference”…”�”}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jåhjÿubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K¬hjËh²hubj)�”}”(hXE* This reference does not own the pointee * It cannot be used to add the graph node to a graph root, nor ``free``'d via ``bpf_obj_drop`` * No explicit control of lifetime, but can infer valid lifetime based on non-owning ref existence (see explanation below) * Access to the pointee's memory will not page fault ”h]”j)�”}”(hhh]”(j)�”}”(hŒŒThis reference does not own the pointee * It cannot be used to add the graph node to a graph root, nor ``free``'d via ``bpf_obj_drop`` ”h]”(hÌ)�”}”(hŒ'This reference does not own the pointee”h]”hŒ'This reference does not own the pointee”…”�”}”(hj!h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K®hjubj)�”}”(hŒa* It cannot be used to add the graph node to a graph root, nor ``free``'d via ``bpf_obj_drop`` ”h]”j)�”}”(hhh]”j)�”}”(hŒ]It cannot be used to add the graph node to a graph root, nor ``free``'d via ``bpf_obj_drop`` ”h]”hÌ)�”}”(hŒ\It cannot be used to add the graph node to a graph root, nor ``free``'d via ``bpf_obj_drop``”h]”(hŒ=It cannot be used to add the graph node to a graph root, nor ”…”�”}”(hj:h²hh³Nh´Nubj)�”}”(hŒ``free``”h]”hŒfree”…”�”}”(hjBh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj:ubhŒ ’d via ”…”�”}”(hj:h²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_drop``”h]”hŒ bpf_obj_drop”…”�”}”(hjTh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj:ubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K°hj6ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj3ubah}”(h]”h ]”h"]”h$]”h&]”j‘j’uh1jh³hÊh´K°hj/ubah}”(h]”h ]”h"]”h$]”h&]”uh1jh³hÊh´K°hjubeh}”(h]”h ]”h"]”h$]”h&]”uh1jhjubj)�”}”(hŒxNo explicit control of lifetime, but can infer valid lifetime based on non-owning ref existence (see explanation below) ”h]”hÌ)�”}”(hŒwNo explicit control of lifetime, but can infer valid lifetime based on non-owning ref existence (see explanation below)”h]”hŒwNo explicit control of lifetime, but can infer valid lifetime based on non-owning ref existence (see explanation below)”…”�”}”(hj„h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K³hj€ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubj)�”}”(hŒ3Access to the pointee's memory will not page fault ”h]”hÌ)�”}”(hŒ2Access to the pointee's memory will not page fault”h]”hŒ4Access to the pointee’s memory will not page fault”…”�”}”(hjœh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K¶hj˜ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjubeh}”(h]”h ]”h"]”h$]”h&]”j‘j’uh1jh³hÊh´K®hjubah}”(h]”h ]”h"]”h$]”h&]”uh1jh³hÊh´K®hjËh²hubhÌ)�”}”(hX™From verifier's perspective non-owning references can only exist between spin_lock and spin_unlock. Why? After spin_unlock another program can do arbitrary operations on the data structure like removing and ``free``-ing via bpf_obj_drop. A non-owning ref to some chunk of memory that was remove'd, ``free``'d, and reused via bpf_obj_new would point to an entirely different thing. Or the memory could go away.”h]”(hŒÑFrom verifier’s perspective non-owning references can only exist between spin_lock and spin_unlock. Why? After spin_unlock another program can do arbitrary operations on the data structure like removing and ”…”�”}”(hj¼h²hh³Nh´Nubj)�”}”(hŒ``free``”h]”hŒfree”…”�”}”(hjÄh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj¼ubhŒU-ing via bpf_obj_drop. A non-owning ref to some chunk of memory that was remove’d, ”…”�”}”(hj¼h²hh³Nh´Nubj)�”}”(hŒ``free``”h]”hŒfree”…”�”}”(hjÖh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj¼ubhŒi’d, and reused via bpf_obj_new would point to an entirely different thing. Or the memory could go away.”…”�”}”(hj¼h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K¸hjËh²hubhÌ)�”}”(hX+To prevent this logic violation all non-owning references are invalidated by the verifier after a critical section ends. This is necessary to ensure the "will not page fault" property of non-owning references. So if the verifier hasn't invalidated a non-owning ref, accessing it will not page fault.”h]”hX1To prevent this logic violation all non-owning references are invalidated by the verifier after a critical section ends. This is necessary to ensure the “will not page faultâ€� property of non-owning references. So if the verifier hasn’t invalidated a non-owning ref, accessing it will not page fault.”…”�”}”(hjîh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´K¿hjËh²hubhÌ)�”}”(hŒìCurrently ``bpf_obj_drop`` is not allowed in the critical section, so if there's a valid non-owning ref, we must be in a critical section, and can conclude that the ref's memory hasn't been dropped-and- ``free``'d or dropped-and-reused.”h]”(hŒ Currently ”…”�”}”(hjüh²hh³Nh´Nubj)�”}”(hŒ``bpf_obj_drop``”h]”hŒ bpf_obj_drop”…”�”}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjüubhŒ· is not allowed in the critical section, so if there’s a valid non-owning ref, we must be in a critical section, and can conclude that the ref’s memory hasn’t been dropped-and- ”…”�”}”(hjüh²hh³Nh´Nubj)�”}”(hŒ``free``”h]”hŒfree”…”�”}”(hjh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjüubhŒ’d or dropped-and-reused.”…”�”}”(hjüh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KÄhjËh²hubhÌ)�”}”(hX½Any reference to a node that is in an rbtree _must_ be non-owning, since the tree has control of the pointee's lifetime. Similarly, any ref to a node that isn't in rbtree _must_ be owning. This results in a nice property: graph API add / remove implementations don't need to check if a node has already been added (or already removed), as the ownership model allows the verifier to prevent such a state from being valid by simply checking types.”h]”hXÃAny reference to a node that is in an rbtree _must_ be non-owning, since the tree has control of the pointee’s lifetime. Similarly, any ref to a node that isn’t in rbtree _must_ be owning. This results in a nice property: graph API add / remove implementations don’t need to check if a node has already been added (or already removed), as the ownership model allows the verifier to prevent such a state from being valid by simply checking types.”…”�”}”(hj.h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KÉhjËh²hubhÌ)�”}”(hŒgHowever, pointer aliasing poses an issue for the above "nice property". Consider the following example:”h]”hŒkHowever, pointer aliasing poses an issue for the above “nice propertyâ€�. Consider the following example:”…”�”}”(hj<h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KÑhjËh²hubj|)�”}”(hXJstruct node_data *n, *m, *o, *p; n = bpf_obj_new(typeof(*n)); /* 1 */ bpf_spin_lock(&lock); bpf_rbtree_add(&tree, n); /* 2 */ m = bpf_rbtree_first(&tree); /* 3 */ o = bpf_rbtree_remove(&tree, n); /* 4 */ p = bpf_rbtree_remove(&tree, m); /* 5 */ bpf_spin_unlock(&lock); bpf_obj_drop(o); bpf_obj_drop(p); /* 6 */”h]”hXJstruct node_data *n, *m, *o, *p; n = bpf_obj_new(typeof(*n)); /* 1 */ bpf_spin_lock(&lock); bpf_rbtree_add(&tree, n); /* 2 */ m = bpf_rbtree_first(&tree); /* 3 */ o = bpf_rbtree_remove(&tree, n); /* 4 */ p = bpf_rbtree_remove(&tree, m); /* 5 */ bpf_spin_unlock(&lock); bpf_obj_drop(o); bpf_obj_drop(p); /* 6 */”…”�”}”hjJsbah}”(h]”h ]”h"]”h$]”h&]”j‹jŒj�‰jŽj�j�}”uh1j{h³hÊh´KÔhjËh²hubhÌ)�”}”(hŒ{Assume the tree is empty before this program runs. If we track verifier state changes here using numbers in above comments:”h]”hŒ{Assume the tree is empty before this program runs. If we track verifier state changes here using numbers in above comments:”…”�”}”(hjYh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KæhjËh²hubj)�”}”(hX 1) n is an owning reference 2) n is a non-owning reference, it's been added to the tree 3) n and m are non-owning references, they both point to the same node 4) o is an owning reference, n and m non-owning, all point to same node 5) o and p are owning, n and m non-owning, all point to the same node 6) a double-free has occurred, since o and p point to same node and o was ``free``'d in previous statement ”h]”hŒenumerated_list”“”)�”}”(hhh]”(j)�”}”(hŒn is an owning reference ”h]”hÌ)�”}”(hŒn is an owning reference”h]”hŒn is an owning reference”…”�”}”(hjth²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kéhjpubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjmubj)�”}”(hŒ9n is a non-owning reference, it's been added to the tree ”h]”hÌ)�”}”(hŒ8n is a non-owning reference, it's been added to the tree”h]”hŒ:n is a non-owning reference, it’s been added to the tree”…”�”}”(hjŒh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Këhjˆubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjmubj)�”}”(hŒDn and m are non-owning references, they both point to the same node ”h]”hÌ)�”}”(hŒCn and m are non-owning references, they both point to the same node”h]”hŒCn and m are non-owning references, they both point to the same node”…”�”}”(hj¤h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kíhj ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjmubj)�”}”(hŒEo is an owning reference, n and m non-owning, all point to same node ”h]”hÌ)�”}”(hŒDo is an owning reference, n and m non-owning, all point to same node”h]”hŒDo is an owning reference, n and m non-owning, all point to same node”…”�”}”(hj¼h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kïhj¸ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjmubj)�”}”(hŒCo and p are owning, n and m non-owning, all point to the same node ”h]”hÌ)�”}”(hŒBo and p are owning, n and m non-owning, all point to the same node”h]”hŒBo and p are owning, n and m non-owning, all point to the same node”…”�”}”(hjÔh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KñhjÐubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjmubj)�”}”(hŒha double-free has occurred, since o and p point to same node and o was ``free``'d in previous statement ”h]”hÌ)�”}”(hŒga double-free has occurred, since o and p point to same node and o was ``free``'d in previous statement”h]”(hŒGa double-free has occurred, since o and p point to same node and o was ”…”�”}”(hjìh²hh³Nh´Nubj)�”}”(hŒ``free``”h]”hŒfree”…”�”}”(hjôh²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjìubhŒ’d in previous statement”…”�”}”(hjìh²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Kóhjèubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjmubeh}”(h]”h ]”h"]”h$]”h&]”Œenumtype”Œarabic”Œprefix”hŒsuffix”Œ)”uh1jkhjgubah}”(h]”h ]”h"]”h$]”h&]”uh1jh³hÊh´KéhjËh²hubhÌ)�”}”(hŒõStates 4 and 5 violate our "nice property", as there are non-owning refs to a node which is not in an rbtree. Statement 5 will try to remove a node which has already been removed as a result of this violation. State 6 is a dangerous double-free.”h]”hŒùStates 4 and 5 violate our “nice propertyâ€�, as there are non-owning refs to a node which is not in an rbtree. Statement 5 will try to remove a node which has already been removed as a result of this violation. State 6 is a dangerous double-free.”…”�”}”(hj# h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KöhjËh²hubhÌ)�”}”(hŒÃAt a minimum we should prevent state 6 from being possible. If we can't also prevent state 5 then we must abandon our "nice property" and check whether a node has already been removed at runtime.”h]”hŒÉAt a minimum we should prevent state 6 from being possible. If we can’t also prevent state 5 then we must abandon our “nice propertyâ€� and check whether a node has already been removed at runtime.”…”�”}”(hj1 h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KûhjËh²hubhÌ)�”}”(hŒ×We prevent both by generalizing the "invalidate non-owning references" behavior of ``bpf_spin_unlock`` and doing similar invalidation after ``bpf_rbtree_remove``. The logic here being that any graph API kfunc which:”h]”(hŒWWe prevent both by generalizing the “invalidate non-owning referencesâ€� behavior of ”…”�”}”(hj? h²hh³Nh´Nubj)�”}”(hŒ``bpf_spin_unlock``”h]”hŒbpf_spin_unlock”…”�”}”(hjG h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj? ubhŒ& and doing similar invalidation after ”…”�”}”(hj? h²hh³Nh´Nubj)�”}”(hŒ``bpf_rbtree_remove``”h]”hŒbpf_rbtree_remove”…”�”}”(hjY h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhj? ubhŒ6. The logic here being that any graph API kfunc which:”…”�”}”(hj? h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´KÿhjËh²hubj)�”}”(hŒ|* takes an arbitrary node argument * removes it from the data structure * returns an owning reference to the removed node ”h]”j)�”}”(hhh]”(j)�”}”(hŒ!takes an arbitrary node argument ”h]”hÌ)�”}”(hŒ takes an arbitrary node argument”h]”hŒ takes an arbitrary node argument”…”�”}”(hj| h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mhjx ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhju ubj)�”}”(hŒ#removes it from the data structure ”h]”hÌ)�”}”(hŒ"removes it from the data structure”h]”hŒ"removes it from the data structure”…”�”}”(hj” h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mhj� ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhju ubj)�”}”(hŒ0returns an owning reference to the removed node ”h]”hÌ)�”}”(hŒ/returns an owning reference to the removed node”h]”hŒ/returns an owning reference to the removed node”…”�”}”(hj¬ h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´Mhj¨ ubah}”(h]”h ]”h"]”h$]”h&]”uh1jhju ubeh}”(h]”h ]”h"]”h$]”h&]”j‘j’uh1jh³hÊh´Mhjq ubah}”(h]”h ]”h"]”h$]”h&]”uh1jh³hÊh´MhjËh²hubhÌ)�”}”(hŒ´May result in a state where some other non-owning reference points to the same node. So ``remove``-type kfuncs must be considered a non-owning reference invalidation point as well.”h]”(hŒXMay result in a state where some other non-owning reference points to the same node. So ”…”�”}”(hjÌ h²hh³Nh´Nubj)�”}”(hŒ ``remove``”h]”hŒremove”…”�”}”(hjÔ h²hh³Nh´Nubah}”(h]”h ]”h"]”h$]”h&]”uh1jhjÌ ubhŒR-type kfuncs must be considered a non-owning reference invalidation point as well.”…”�”}”(hjÌ h²hh³Nh´Nubeh}”(h]”h ]”h"]”h$]”h&]”uh1hËh³hÊh´M hjËh²hubeh}”(h]”j”ah ]”h"]”Œnon-owning references”ah$]”h&]”uh1hµhh·h²hh³hÊh´KRubeh}”(h]”Œbpf-graph-data-structures”ah ]”h"]”Œbpf graph data structures”ah$]”h&]”uh1hµhhh²hh³hÊh´Kubeh}”(h]”h ]”h"]”h$]”h&]”Œsource”hÊuh1hŒcurrent_source”NŒ current_line”NŒsettings”Œdocutils.frontend”ŒValues”“”)�”}”(hºNŒ generator”NŒ datestamp”NŒ source_link”NŒ source_url”NŒ toc_backlinks”Œentry”Œfootnote_backlinks”KŒ sectnum_xform”KŒstrip_comments”NŒstrip_elements_with_classes”NŒ strip_classes”NŒ report_level”KŒ halt_level”KŒexit_status_level”KŒdebug”NŒwarning_stream”NŒ traceback”ˆŒinput_encoding”Œ utf-8-sig”Œinput_encoding_error_handler”Œstrict”Œoutput_encoding”Œutf-8”Œoutput_encoding_error_handler”j Œerror_encoding”Œutf-8”Œerror_encoding_error_handler”Œbackslashreplace”Œ language_code”Œen”Œrecord_dependencies”NŒconfig”NŒ id_prefix”hŒauto_id_prefix”Œid”Œ dump_settings”NŒdump_internals”NŒdump_transforms”NŒdump_pseudo_xml”NŒexpose_internals”NŒstrict_visitor”NŒ_disable_config”NŒ_source”hÊŒ _destination”NŒ _config_files”]”Œ7/var/lib/git/docbuild/linux/Documentation/docutils.conf”aŒfile_insertion_enabled”ˆŒ raw_enabled”KŒline_length_limit”M'Œpep_references”NŒ pep_base_url”Œhttps://peps.python.org/”Œpep_file_url_template”Œpep-%04d”Œrfc_references”NŒ rfc_base_url”Œ&https://datatracker.ietf.org/doc/html/”Œ tab_width”KŒtrim_footnote_reference_space”‰Œsyntax_highlight”Œlong”Œ smart_quotes”ˆŒsmartquotes_locales”]”Œcharacter_level_inline_markup”‰Œdoctitle_xform”‰Œ docinfo_xform”KŒsectsubtitle_xform”‰Œ image_loading”Œlink”Œembed_stylesheet”‰Œcloak_email_addresses”ˆŒsection_self_link”‰Œenv”NubŒreporter”NŒindirect_targets”]”Œsubstitution_defs”}”Œsubstitution_names”}”Œrefnames”}”Œrefids”}”Œnameids”}”(jø jõ j®j©jÿj.jYjPjÈjrjð j”uŒ nametypes”}”(jø ‰j®‰jÿ‰jY‰jȉjð ‰uh}”(jõ h·j©j j.j±jPjjrj\j”jËj(jjJjAjljcjŽj…uŒ footnote_refs”}”Œ citation_refs”}”Œ autofootnotes”]”Œautofootnote_refs”]”Œsymbol_footnotes”]”Œsymbol_footnote_refs”]”Œ footnotes”]”Œ citations”]”Œautofootnote_start”KŒsymbol_footnote_start”KŒ id_counter”Œ collections”ŒCounter”“”}”j, Ks…”R”Œparse_messages”]”Œtransform_messages”]”Œ transformer”NŒ include_log”]”Œ decoration”Nh²hub.