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) <[email protected]>
---
 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


Reply via email to