CVE-2024-41010

Severity CVSS v4.0:
Pending analysis
Type:
CWE-416 Use After Free
Publication date:
17/07/2024
Last modified:
19/07/2024

Description

In the Linux kernel, the following vulnerability has been resolved:<br /> <br /> bpf: Fix too early release of tcx_entry<br /> <br /> Pedro Pinto and later independently also Hyunwoo Kim and Wongi Lee reported<br /> an issue that the tcx_entry can be released too early leading to a use<br /> after free (UAF) when an active old-style ingress or clsact qdisc with a<br /> shared tc block is later replaced by another ingress or clsact instance.<br /> <br /> Essentially, the sequence to trigger the UAF (one example) can be as follows:<br /> <br /> 1. A network namespace is created<br /> 2. An ingress qdisc is created. This allocates a tcx_entry, and<br /> &amp;tcx_entry-&gt;miniq is stored in the qdisc&amp;#39;s miniqp-&gt;p_miniq. At the<br /> same time, a tcf block with index 1 is created.<br /> 3. chain0 is attached to the tcf block. chain0 must be connected to<br /> the block linked to the ingress qdisc to later reach the function<br /> tcf_chain0_head_change_cb_del() which triggers the UAF.<br /> 4. Create and graft a clsact qdisc. This causes the ingress qdisc<br /> created in step 1 to be removed, thus freeing the previously linked<br /> tcx_entry:<br /> <br /> rtnetlink_rcv_msg()<br /> =&gt; tc_modify_qdisc()<br /> =&gt; qdisc_create()<br /> =&gt; clsact_init() [a]<br /> =&gt; qdisc_graft()<br /> =&gt; qdisc_destroy()<br /> =&gt; __qdisc_destroy()<br /> =&gt; ingress_destroy() [b]<br /> =&gt; tcx_entry_free()<br /> =&gt; kfree_rcu() // tcx_entry freed<br /> <br /> 5. Finally, the network namespace is closed. This registers the<br /> cleanup_net worker, and during the process of releasing the<br /> remaining clsact qdisc, it accesses the tcx_entry that was<br /> already freed in step 4, causing the UAF to occur:<br /> <br /> cleanup_net()<br /> =&gt; ops_exit_list()<br /> =&gt; default_device_exit_batch()<br /> =&gt; unregister_netdevice_many()<br /> =&gt; unregister_netdevice_many_notify()<br /> =&gt; dev_shutdown()<br /> =&gt; qdisc_put()<br /> =&gt; clsact_destroy() [c]<br /> =&gt; tcf_block_put_ext()<br /> =&gt; tcf_chain0_head_change_cb_del()<br /> =&gt; tcf_chain_head_change_item()<br /> =&gt; clsact_chain_head_change()<br /> =&gt; mini_qdisc_pair_swap() // UAF<br /> <br /> There are also other variants, the gist is to add an ingress (or clsact)<br /> qdisc with a specific shared block, then to replace that qdisc, waiting<br /> for the tcx_entry kfree_rcu() to be executed and subsequently accessing<br /> the current active qdisc&amp;#39;s miniq one way or another.<br /> <br /> The correct fix is to turn the miniq_active boolean into a counter. What<br /> can be observed, at step 2 above, the counter transitions from 0-&gt;1, at<br /> step [a] from 1-&gt;2 (in order for the miniq object to remain active during<br /> the replacement), then in [b] from 2-&gt;1 and finally [c] 1-&gt;0 with the<br /> eventual release. The reference counter in general ranges from [0,2] and<br /> it does not need to be atomic since all access to the counter is protected<br /> by the rtnl mutex. With this in place, there is no longer a UAF happening<br /> and the tcx_entry is freed at the correct time.

Vulnerable products and versions

CPE From Up to
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.6 (including) 6.6.41 (excluding)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 6.7 (including) 6.9.10 (excluding)