{"id":"CVE-2026-80975","state":"PUBLISHED","title":"mfd: qnap-mcu: keep the reply buffer alive past a command timeout","description":"In the Linux kernel, the following vulnerability has been resolved:\n\nmfd: qnap-mcu: keep the reply buffer alive past a command timeout\n\nqnap_mcu_exec() publishes an on-stack buffer to the receive path:\n\n\tunsigned char rx[QNAP_MCU_RX_BUFFER_SIZE];\n\t...\n\treply->data = rx;\n\treply->length = length;\n\nand qnap_mcu_receive_buf() writes into it from the serdev receive path,\nwhich runs out of flush_to_ldisc() and is not serialized against\nqnap_mcu_exec() at all. bus_lock cannot cover it, because qnap_mcu_exec()\nholds that mutex across wait_for_completion_timeout().\n\nOn a timeout qnap_mcu_exec() returns with reply->data still pointing at\nits own frame. A reply that arrives late, or an unsolicited message from\nthe MCU, is then written into a stack frame that has been left, corrupting\nwhatever runs next on that stack. The same applies when qnap_mcu_write()\nfails, since that path returns without touching the reply state either.\n\nMove the receive buffer into struct qnap_mcu. It is 37 bytes and the\nstructure is devm_kzalloc()ed, so it lives as long as the driver, and a\nlate write lands in memory that is still valid and is reinitialized by the\nnext command. bus_lock keeps commands from sharing it.\n\nThis deliberately does not clear reply->data or reply->length on the\ntimeout path. Doing so races with qnap_mcu_receive_buf(), which reads both\nafter its\n\n\tif (!reply->length)\n\t\treturn size;\n\ncheck: clearing reply->data gives a NULL dereference, and clearing\nreply->length alone removes the reply->received == reply->length exit\ncondition, so the copy loop runs until the uart chunk is consumed and\noverruns the buffer. Leaving both set keeps the write bounded by\nreply->length, which qnap_mcu_exec() has already checked against\nsizeof(mcu->rx).","publishedAt":"2026-09-11T19:42:37.086Z","updatedAt":"2026-10-19T08:45:59.772Z","severity":{"score":7.8,"label":"High","version":"3.1","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","source":"Linux"},"assigner":"Linux","products":["Linux Linux"],"affected":[{"vendor":"Linux","product":"Linux","defaultStatus":"unaffected","versions":[{"version":"998f70d1806bb718a7565f350283e4a79c8cbb4b","status":"affected","lessThan":"0b6680e306397097a97767447368221fac753809","versionType":"git"},{"version":"998f70d1806bb718a7565f350283e4a79c8cbb4b","status":"affected","lessThan":"8391ee06d08845a0a165b5fe679ba326915166b2","versionType":"git"},{"version":"998f70d1806bb718a7565f350283e4a79c8cbb4b","status":"affected","lessThan":"47504742cea7878ebd1bf1491bbed923df6b90b1","versionType":"git"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["drivers/mfd/qnap-mcu.c"]},{"vendor":"Linux","product":"Linux","defaultStatus":"affected","versions":[{"version":"6.14","status":"affected"},{"version":"0","status":"unaffected","lessThan":"6.14","versionType":"semver"},{"version":"6.18.51","status":"unaffected","lessThanOrEqual":"6.18.*","versionType":"semver"},{"version":"7.2.4","status":"unaffected","lessThanOrEqual":"7.2.*","versionType":"semver"},{"version":"7.3","status":"unaffected","lessThanOrEqual":"*","versionType":"original_commit_for_fix"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["drivers/mfd/qnap-mcu.c"]}],"references":["https://git.kernel.org/stable/c/0b6680e306397097a97767447368221fac753809","https://git.kernel.org/stable/c/8391ee06d08845a0a165b5fe679ba326915166b2","https://git.kernel.org/stable/c/47504742cea7878ebd1bf1491bbed923df6b90b1"],"weaknesses":[],"ingestedAt":"2026-10-29T20:13:07.220Z","source":{"repository":"CVEProject/cvelistV5","path":"cves/2026/80xxx/CVE-2026-80975.json","commit":"556c9d3a8cd17e218bb8e452ba7cd619a384ff47"},"raw":{"dataType":"CVE_RECORD","dataVersion":"5.2","cveMetadata":{"cveId":"CVE-2026-80975","state":"PUBLISHED","datePublished":"2026-09-11T19:42:37.086Z","dateUpdated":"2026-10-19T08:45:59.772Z","assignerShortName":"Linux","assignerOrgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","dateReserved":"2026-08-26T14:34:25.810Z"},"containers":{"cna":{"title":"mfd: qnap-mcu: keep the reply buffer alive past a command timeout","descriptions":[{"lang":"en","value":"In the Linux kernel, the following vulnerability has been resolved:\n\nmfd: qnap-mcu: keep the reply buffer alive past a command timeout\n\nqnap_mcu_exec() publishes an on-stack buffer to the receive path:\n\n\tunsigned char rx[QNAP_MCU_RX_BUFFER_SIZE];\n\t...\n\treply->data = rx;\n\treply->length = length;\n\nand qnap_mcu_receive_buf() writes into it from the serdev receive path,\nwhich runs out of flush_to_ldisc() and is not serialized against\nqnap_mcu_exec() at all. bus_lock cannot cover it, because qnap_mcu_exec()\nholds that mutex across wait_for_completion_timeout().\n\nOn a timeout qnap_mcu_exec() returns with reply->data still pointing at\nits own frame. A reply that arrives late, or an unsolicited message from\nthe MCU, is then written into a stack frame that has been left, corrupting\nwhatever runs next on that stack. The same applies when qnap_mcu_write()\nfails, since that path returns without touching the reply state either.\n\nMove the receive buffer into struct qnap_mcu. It is 37 bytes and the\nstructure is devm_kzalloc()ed, so it lives as long as the driver, and a\nlate write lands in memory that is still valid and is reinitialized by the\nnext command. bus_lock keeps commands from sharing it.\n\nThis deliberately does not clear reply->data or reply->length on the\ntimeout path. Doing so races with qnap_mcu_receive_buf(), which reads both\nafter its\n\n\tif (!reply->length)\n\t\treturn size;\n\ncheck: clearing reply->data gives a NULL dereference, and clearing\nreply->length alone removes the reply->received == reply->length exit\ncondition, so the copy loop runs until the uart chunk is consumed and\noverruns the buffer. Leaving both set keeps the write bounded by\nreply->length, which qnap_mcu_exec() has already checked against\nsizeof(mcu->rx)."}],"affected":[{"vendor":"Linux","product":"Linux","defaultStatus":"unaffected","versions":[{"version":"998f70d1806bb718a7565f350283e4a79c8cbb4b","status":"affected","lessThan":"0b6680e306397097a97767447368221fac753809","versionType":"git"},{"version":"998f70d1806bb718a7565f350283e4a79c8cbb4b","status":"affected","lessThan":"8391ee06d08845a0a165b5fe679ba326915166b2","versionType":"git"},{"version":"998f70d1806bb718a7565f350283e4a79c8cbb4b","status":"affected","lessThan":"47504742cea7878ebd1bf1491bbed923df6b90b1","versionType":"git"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["drivers/mfd/qnap-mcu.c"]},{"vendor":"Linux","product":"Linux","defaultStatus":"affected","versions":[{"version":"6.14","status":"affected"},{"version":"0","status":"unaffected","lessThan":"6.14","versionType":"semver"},{"version":"6.18.51","status":"unaffected","lessThanOrEqual":"6.18.*","versionType":"semver"},{"version":"7.2.4","status":"unaffected","lessThanOrEqual":"7.2.*","versionType":"semver"},{"version":"7.3","status":"unaffected","lessThanOrEqual":"*","versionType":"original_commit_for_fix"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["drivers/mfd/qnap-mcu.c"]}],"references":[{"url":"https://git.kernel.org/stable/c/0b6680e306397097a97767447368221fac753809"},{"url":"https://git.kernel.org/stable/c/8391ee06d08845a0a165b5fe679ba326915166b2"},{"url":"https://git.kernel.org/stable/c/47504742cea7878ebd1bf1491bbed923df6b90b1"}],"metrics":[{"cvssV3_1":{"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"},"scenarios":[{"lang":"en","value":"AV:L - The bug is reached only through local qnap_mcu_exec() calls from hwmon/nvmem/LED/input/thermal sysfs on QNAP NAS devices whose MCU is a soldered UART serdev child, not a network protocol or a user-pluggable USB device, so the vector is Local rather than Physical.\nAC:L - This is use-after-return of an on-stack RX buffer published to qnap_mcu_receive_buf(), which runs from flush_to_ldisc() with no lock shared with qnap_mcu_exec(); an attacker issues commands via sysfs, retries at will, and after the 500 ms timeout reply->data still points at their kernel stack so a late or unsolicited MCU chunk writes into that frame.\nPR:L - Fan/temp hwmon attributes are mode 0444 and pwm is 0644, and the EEPROM nvmem is registered with root_only=false (0444), so any unprivileged local user can invoke qnap_mcu_exec() by reading those sysfs files without capabilities or init-namespace root.\nUI:N - Exploitation uses the attacker’s own reads of already-present hwmon and nvmem sysfs; no victim must mount a filesystem, plug in hardware, or otherwise act, and thermal cooling plus input polling can also enter the same path automatically.\nS:U - The use-after-return corrupts the kernel stack of the task or kworker that called qnap_mcu_exec() on the same host; it does not cross a VM, IOMMU, or sandbox boundary into a separate security authority.\nC:H - A late UART write of up to 37 bytes into a returned kernel stack frame is use-after-return memory corruption; the attacker can groom that stack with a follow-up syscall so the overwrite becomes a kernel read primitive, which per UAF guidance is High confidentiality impact.\nI:H - The same write into the abandoned stack can smash return addresses, saved registers, and pointers of the next function on that stack, including after attacker-controlled syscall grooming, yielding control-flow hijack; use-after-return stack corruption is High integrity impact.\nA:H - Writing MCU UART bytes into a kernel stack frame that has already been left corrupts in-use stack state and causes an oops or panic even when not turned into a full exploit, fully denying availability."}]}],"providerMetadata":{"shortName":"Linux","orgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","dateUpdated":"2026-10-19T08:45:59.772Z"},"cpeApplicability":[{"nodes":[{"operator":"OR","negate":false,"cpeMatch":[{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.14","versionEndExcluding":"6.18.51"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.14","versionEndExcluding":"7.2.4"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.14","versionEndExcluding":"7.3"}]}]}],"x_generator":{"engine":"bippy-1.2.0"}}}}}