[PATCH RESEND v2 03/26] PCI/proc: let procfs copy in config space writes
From: Christian Brauner
Date: Wed Nov 04 2026 - 15:37:13 EST
Implement ->proc_write_buf() so procfs copies in what is written before
it pins the entry, and write the config space from that copy with
get_unaligned_le16() and get_unaligned_le32() instead of get_user().
The copy holds at most PCI_CFG_SPACE_EXP_SIZE bytes. Larger writes come
up short with PROC_ENTRY_SHORT_WRITES, like they did before because
they were cut at the end of the config space.
A buffer that can't be read used to write zeroes to the config space and
report success. It fails with -EFAULT now and nothing is written.
Signed-off-by: Christian Brauner (Amutable) <brauner@xxxxxxxxxx>
---
drivers/pci/proc.c | 32 +++++++++++---------------------
1 file changed, 11 insertions(+), 21 deletions(-)
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index e0dc70123f82..d420353ee2b3 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -16,6 +16,7 @@
#include <linux/security.h>
#include <linux/panic.h>
#include <linux/sched.h>
+#include <linux/unaligned.h>
#include <asm/byteorder.h>
#include "pci.h"
@@ -114,8 +115,8 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
return nbytes;
}
-static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
- size_t nbytes, loff_t *ppos)
+static ssize_t proc_bus_pci_write(struct file *file, char *buf, size_t nbytes,
+ loff_t *ppos)
{
struct inode *ino = file_inode(file);
struct pci_dev *dev = pde_data(ino);
@@ -145,51 +146,38 @@ 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;
-
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);
+ pci_user_write_config_byte(dev, pos, *buf);
buf++;
pos++;
cnt--;
}
if ((pos & 3) && cnt > 2) {
- __le16 val;
- get_user(val, (__le16 __user *) buf);
- pci_user_write_config_word(dev, pos, le16_to_cpu(val));
+ pci_user_write_config_word(dev, pos, get_unaligned_le16(buf));
buf += 2;
pos += 2;
cnt -= 2;
}
while (cnt >= 4) {
- __le32 val;
- get_user(val, (__le32 __user *) buf);
- pci_user_write_config_dword(dev, pos, le32_to_cpu(val));
+ pci_user_write_config_dword(dev, pos, get_unaligned_le32(buf));
buf += 4;
pos += 4;
cnt -= 4;
}
if (cnt >= 2) {
- __le16 val;
- get_user(val, (__le16 __user *) buf);
- pci_user_write_config_word(dev, pos, le16_to_cpu(val));
+ pci_user_write_config_word(dev, pos, get_unaligned_le16(buf));
buf += 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, *buf);
pos++;
}
@@ -337,9 +325,11 @@ static int proc_bus_pci_release(struct inode *inode, struct file *file)
#endif /* HAVE_PCI_MMAP */
static const struct proc_ops proc_bus_pci_ops = {
+ .proc_flags = PROC_ENTRY_SHORT_WRITES,
.proc_lseek = proc_bus_pci_lseek,
.proc_read = proc_bus_pci_read,
- .proc_write = proc_bus_pci_write,
+ .proc_write_buf = proc_bus_pci_write,
+ .proc_write_size = PCI_CFG_SPACE_EXP_SIZE,
.proc_ioctl = proc_bus_pci_ioctl,
#ifdef CONFIG_COMPAT
.proc_compat_ioctl = proc_bus_pci_ioctl,
--
2.53.0