From: syzbot <syzbot+c7604c9fdd7580cca4e0@syzkaller.appspotmail.com>
To: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com
Subject: Forwarded: [PATCH] PCI/proc: validate user buffer before touching config space
Date: Fri, 01 May 2026 16:47:32 -0700 [thread overview]
Message-ID: <69f53b94.050a0220.312cd3.001b.GAE@google.com> (raw)
In-Reply-To: <69f3f165.170a0220.5f1b.0010.GAE@google.com>
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.
***
Subject: [PATCH] PCI/proc: validate user buffer before touching config space
Author: kartikey406@gmail.com
#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
proc_bus_pci_write() invokes __get_user() repeatedly without checking
its return value. When the user pointer faults, the extable fixup
leaves the destination indeterminate but the function still hands the
value to pci_user_write_config_*(), writing fixup state to PCI
configuration space.
syzbot triggers this with a writev() whose iov_base is NULL on
/proc/bus/pci/00/03.0 (the virtio-blk controller in the syzkaller VM).
Every __get_user() faults, val ends up as fixup-zero, and zero is
written to config space offsets 0..6 -- including the Command register
at offset 4, clearing Bus Master and Memory Space Enable. The disk
goes silent mid-flight, in-flight journal bios never complete, and
jbd2 hangs in wait_on_buffer() indefinitely:
INFO: task jbd2/sda1-8:4955 blocked in I/O wait for more than 143 seconds.
__wait_on_buffer fs/buffer.c:123
jbd2_journal_commit_transaction+0x388a/0x6870 fs/jbd2/commit.c:837
kjournald2 fs/jbd2/journal.c:201
proc_bus_pci_read() has the symmetric problem: an unchecked __put_user()
into a faulting user pointer silently drops config-space data.
Fix both by staging through a kernel buffer. The write path uses
memdup_user() to copy the user buffer up front; a bad user pointer now
returns -EFAULT before any device state is touched, and the function
no longer races a concurrent userspace mutation of the buffer across
successive __get_user()s. The read path collects config-space data
into a kmalloc'd buffer and copy_to_user()s it once at the end.
Reported-by: syzbot+c7604c9fdd7580cca4e0@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c7604c9fdd7580cca4e0
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
drivers/pci/proc.c | 78 ++++++++++++++++++++++++----------------------
1 file changed, 41 insertions(+), 37 deletions(-)
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index ce36e35681e8..aa54c4e1445d 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -31,13 +31,14 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
struct pci_dev *dev = pde_data(file_inode(file));
unsigned int pos = *ppos;
unsigned int cnt, size;
+ void *kbuf;
+ u8 *kp;
/*
* Normal users can read only the standardized portion of the
* configuration space as several chips lock up when trying to read
* undefined locations (think of Intel PIIX4 as a typical example).
*/
-
if (capable(CAP_SYS_ADMIN))
size = dev->cfg_size;
else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
@@ -53,57 +54,61 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
nbytes = size - pos;
cnt = nbytes;
- if (!access_ok(buf, cnt))
- return -EINVAL;
+ kbuf = kmalloc(cnt, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+ kp = kbuf;
pci_config_pm_runtime_get(dev);
if ((pos & 1) && cnt) {
- unsigned char val;
- pci_user_read_config_byte(dev, pos, &val);
- __put_user(val, buf);
- buf++;
+ pci_user_read_config_byte(dev, pos, kp);
+ kp++;
pos++;
cnt--;
}
if ((pos & 3) && cnt > 2) {
- unsigned short val;
+ u16 val;
pci_user_read_config_word(dev, pos, &val);
- __put_user(cpu_to_le16(val), (__le16 __user *) buf);
- buf += 2;
+ *(__le16 *)kp = cpu_to_le16(val);
+ kp += 2;
pos += 2;
cnt -= 2;
}
while (cnt >= 4) {
- unsigned int val;
+ u32 val;
pci_user_read_config_dword(dev, pos, &val);
- __put_user(cpu_to_le32(val), (__le32 __user *) buf);
- buf += 4;
+ *(__le32 *)kp = cpu_to_le32(val);
+ kp += 4;
pos += 4;
cnt -= 4;
cond_resched();
}
if (cnt >= 2) {
- unsigned short val;
+ u16 val;
pci_user_read_config_word(dev, pos, &val);
- __put_user(cpu_to_le16(val), (__le16 __user *) buf);
- buf += 2;
+ *(__le16 *)kp = cpu_to_le16(val);
+ kp += 2;
pos += 2;
cnt -= 2;
}
if (cnt) {
- unsigned char val;
- pci_user_read_config_byte(dev, pos, &val);
- __put_user(val, buf);
+ pci_user_read_config_byte(dev, pos, kp);
pos++;
}
pci_config_pm_runtime_put(dev);
+ if (copy_to_user(buf, kbuf, nbytes)) {
+ kfree(kbuf);
+ return -EFAULT;
+ }
+ kfree(kbuf);
+
*ppos = pos;
return nbytes;
}
@@ -116,6 +121,8 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
int pos = *ppos;
int size = dev->cfg_size;
int cnt, ret;
+ const u8 *kp;
+ u8 *kbuf;
ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
if (ret)
@@ -129,56 +136,53 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
nbytes = size - pos;
cnt = nbytes;
- if (!access_ok(buf, cnt))
- return -EINVAL;
+ kbuf = memdup_user(buf, cnt);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
+ kp = kbuf;
pci_config_pm_runtime_get(dev);
if ((pos & 1) && cnt) {
- unsigned char val;
- __get_user(val, buf);
- pci_user_write_config_byte(dev, pos, val);
- buf++;
+ pci_user_write_config_byte(dev, pos, *kp);
+ kp++;
pos++;
cnt--;
}
if ((pos & 3) && cnt > 2) {
- __le16 val;
- __get_user(val, (__le16 __user *) buf);
+ __le16 val = *(const __le16 *)kp;
pci_user_write_config_word(dev, pos, le16_to_cpu(val));
- buf += 2;
+ kp += 2;
pos += 2;
cnt -= 2;
}
while (cnt >= 4) {
- __le32 val;
- __get_user(val, (__le32 __user *) buf);
+ __le32 val = *(const __le32 *)kp;
pci_user_write_config_dword(dev, pos, le32_to_cpu(val));
- buf += 4;
+ kp += 4;
pos += 4;
cnt -= 4;
}
if (cnt >= 2) {
- __le16 val;
- __get_user(val, (__le16 __user *) buf);
+ __le16 val = *(const __le16 *)kp;
pci_user_write_config_word(dev, pos, le16_to_cpu(val));
- buf += 2;
+ kp += 2;
pos += 2;
cnt -= 2;
}
if (cnt) {
- unsigned char val;
- __get_user(val, buf);
- pci_user_write_config_byte(dev, pos, val);
+ pci_user_write_config_byte(dev, pos, *kp);
pos++;
}
pci_config_pm_runtime_put(dev);
+ kfree(kbuf);
+
*ppos = pos;
i_size_write(ino, dev->cfg_size);
return nbytes;
--
2.43.0
next prev parent reply other threads:[~2026-05-01 23:47 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-01 0:18 [syzbot] INFO: task jbd2/sda1-NUM:NUM blocked in I/O wait for more than NUM seconds syzbot
2026-05-01 14:48 ` [syzbot] [ext4?] " syzbot
2026-05-01 23:47 ` syzbot [this message]
2026-05-02 0:02 ` Forwarded: [PATCH] PCI/proc: check return value of __get_user() in proc_bus_pci_write() syzbot
2026-05-04 1:22 ` Forwarded: [PATCH v2] PCI/proc: check user access return values in proc_bus_pci_{read,write}() syzbot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=69f53b94.050a0220.312cd3.001b.GAE@google.com \
--to=syzbot+c7604c9fdd7580cca4e0@syzkaller.appspotmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=syzkaller-bugs@googlegroups.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.