[PATCH RFC 01/38] PCI/proc: don't truncate the position of config space accesses
From: Christian Brauner
Date: Mon Nov 02 2026 - 09:28:27 EST
proc_bus_pci_write() truncates the file position to an int. A pwrite()
at an offset like 0xfffff000 turns it negative, the check against the
size of the config space passes and the count it clamps to ends up
larger than what was written. The loop then reads past the end of the
caller's buffer and writes all of it into config space at negative
offsets, which pci_user_write_config_*() pass on to the config
accessors unchecked. The write also returns more than it was asked to
write.
An offset above 4G that truncates to a small positive value writes to
the start of config space instead. proc_bus_pci_read() keeps the
position in an unsigned int, so a read at such an offset returns the
start of config space instead of hitting EOF.
Check the position against the size of the config space before
narrowing it.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Christian Brauner (Amutable) <brauner@xxxxxxxxxx>
---
drivers/pci/proc.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 3d7a9ed5fc22..e0dc70123f82 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -32,8 +32,7 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
size_t nbytes, loff_t *ppos)
{
struct pci_dev *dev = pde_data(file_inode(file));
- unsigned int pos = *ppos;
- unsigned int cnt, size;
+ unsigned int pos, cnt, size;
/*
* Normal users can read only the standardized portion of the
@@ -51,8 +50,9 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
if (!nbytes)
return 0;
- if (pos >= size)
+ if (*ppos >= size)
return 0;
+ pos = *ppos;
if (nbytes >= size)
nbytes = size;
if (pos + nbytes > size)
@@ -119,9 +119,8 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
{
struct inode *ino = file_inode(file);
struct pci_dev *dev = pde_data(ino);
- int pos = *ppos;
int size = dev->cfg_size;
- int cnt, ret;
+ int pos, cnt, ret;
ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
if (ret)
@@ -130,14 +129,16 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
if (!nbytes)
return 0;
+ if (*ppos >= size)
+ return 0;
+ pos = *ppos;
+
if (resource_is_exclusive(&dev->driver_exclusive_resource, pos, nbytes)) {
pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %x",
current->comm, pos);
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
}
- if (pos >= size)
- return 0;
if (nbytes >= size)
nbytes = size;
if (pos + nbytes > size)
--
2.53.0