From 9a450484089dfa8b6348eff2a918f3c8f38585b9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 27 Nov 2017 12:29:50 +0100 Subject: lp: support 64-bit time_t user space Once we get a glibc with 64-bit time_t, the LPSETTIMEOUT ioctl stops working, since the command number and data structure no longer match. To work around that, this introduces a new command number LPSETTIMEOUT_NEW that is used whenever the modified user space evaluates the LPSETTIMEOUT macro. The trick we use is a bit convoluted but necessary: we cannot check for any macros set by the C library in linux/lp.h, because this particular header can be included before including sys/time.h. However, we can assume that by the time that LPSETTIMEOUT is seen in the code, the definition for 'timeval' and 'time_t' has been seen as well, so we can use the sizeof() operator to determine whether we should use the old or the new definition. We use the old one not only for traditional 32-bit user space with 32-bit time_t, but also for all 64-bit architectures and x32, which always use a 64-bit time_t, the new definition will be used only for 32-bit user space with 64-bit time_t, which also requires a newer kernel. The compat_ioctl() handler now implements both commands, but has to use a special case for existing x32 binaries. The native ioctl handler now implements both command numbers on both 32-bit and 64-bit, though the latter version use the same interpretation for both. This is based on an earlier patch from Bamvor. Cc: Bamvor Jian Zhang Link: http://www.spinics.net/lists/y2038/msg01162.html Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/char/lp.c | 67 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 16 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 8249762192d5..be14abf70da1 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -659,17 +659,31 @@ static int lp_do_ioctl(unsigned int minor, unsigned int cmd, return retval; } -static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout) +static int lp_set_timeout(unsigned int minor, s64 tv_sec, long tv_usec) { long to_jiffies; /* Convert to jiffies, place in lp_table */ - if ((par_timeout->tv_sec < 0) || - (par_timeout->tv_usec < 0)) { + if (tv_sec < 0 || tv_usec < 0) return -EINVAL; + + /* + * we used to not check, so let's not make this fatal, + * but deal with user space passing a 32-bit tv_nsec in + * a 64-bit field, capping the timeout to 1 second + * worth of microseconds, and capping the total at + * MAX_JIFFY_OFFSET. + */ + if (tv_usec > 999999) + tv_usec = 999999; + + if (tv_sec >= MAX_SEC_IN_JIFFIES - 1) { + to_jiffies = MAX_JIFFY_OFFSET; + } else { + to_jiffies = DIV_ROUND_UP(tv_usec, 1000000/HZ); + to_jiffies += tv_sec * (long) HZ; } - to_jiffies = DIV_ROUND_UP(par_timeout->tv_usec, 1000000/HZ); - to_jiffies += par_timeout->tv_sec * (long) HZ; + if (to_jiffies <= 0) { return -EINVAL; } @@ -677,23 +691,43 @@ static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout) return 0; } +static int lp_set_timeout32(unsigned int minor, void __user *arg) +{ + s32 karg[2]; + + if (copy_from_user(karg, arg, sizeof(karg))) + return -EFAULT; + + return lp_set_timeout(minor, karg[0], karg[1]); +} + +static int lp_set_timeout64(unsigned int minor, void __user *arg) +{ + s64 karg[2]; + + if (copy_from_user(karg, arg, sizeof(karg))) + return -EFAULT; + + return lp_set_timeout(minor, karg[0], karg[1]); +} + static long lp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor; - struct timeval par_timeout; int ret; minor = iminor(file_inode(file)); mutex_lock(&lp_mutex); switch (cmd) { - case LPSETTIMEOUT: - if (copy_from_user(&par_timeout, (void __user *)arg, - sizeof (struct timeval))) { - ret = -EFAULT; + case LPSETTIMEOUT_OLD: + if (BITS_PER_LONG == 32) { + ret = lp_set_timeout32(minor, (void __user *)arg); break; } - ret = lp_set_timeout(minor, &par_timeout); + /* fallthrough for 64-bit */ + case LPSETTIMEOUT_NEW: + ret = lp_set_timeout64(minor, (void __user *)arg); break; default: ret = lp_do_ioctl(minor, cmd, arg, (void __user *)arg); @@ -709,18 +743,19 @@ static long lp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor; - struct timeval par_timeout; int ret; minor = iminor(file_inode(file)); mutex_lock(&lp_mutex); switch (cmd) { - case LPSETTIMEOUT: - if (compat_get_timeval(&par_timeout, compat_ptr(arg))) { - ret = -EFAULT; + case LPSETTIMEOUT_OLD: + if (!COMPAT_USE_64BIT_TIME) { + ret = lp_set_timeout32(minor, (void __user *)arg); break; } - ret = lp_set_timeout(minor, &par_timeout); + /* fallthrough for x32 mode */ + case LPSETTIMEOUT_NEW: + ret = lp_set_timeout64(minor, (void __user *)arg); break; #ifdef LP_STATS case LPGETSTATS: -- cgit v1.2.3-55-g7522 From 22ec1a2aea73b9dfe340dff7945bd85af4cc6280 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 1 Dec 2017 13:19:39 -0800 Subject: /dev/mem: Add bounce buffer for copy-out As done for /proc/kcore in commit df04abfd181a ("fs/proc/kcore.c: Add bounce buffer for ktext data") this adds a bounce buffer when reading memory via /dev/mem. This is needed to allow kernel text memory to be read out when built with CONFIG_HARDENED_USERCOPY (which refuses to read out kernel text) and without CONFIG_STRICT_DEVMEM (which would have refused to read any RAM contents at all). Since this build configuration isn't common (most systems with CONFIG_HARDENED_USERCOPY also have CONFIG_STRICT_DEVMEM), this also tries to inform Kconfig about the recommended settings. This patch is modified from Brad Spengler/PaX Team's changes to /dev/mem code in the last public patch of grsecurity/PaX based on my understanding of the code. Changes or omissions from the original code are mine and don't reflect the original grsecurity/PaX code. Reported-by: Michael Holzheu Fixes: f5509cc18daa ("mm: Hardened usercopy") Signed-off-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 27 ++++++++++++++++++++++----- security/Kconfig | 1 + 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6aefe5370e5b..052011bcf100 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -107,6 +107,8 @@ static ssize_t read_mem(struct file *file, char __user *buf, phys_addr_t p = *ppos; ssize_t read, sz; void *ptr; + char *bounce; + int err; if (p != *ppos) return 0; @@ -129,15 +131,22 @@ static ssize_t read_mem(struct file *file, char __user *buf, } #endif + bounce = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!bounce) + return -ENOMEM; + while (count > 0) { unsigned long remaining; int allowed; sz = size_inside_page(p, count); + err = -EPERM; allowed = page_is_allowed(p >> PAGE_SHIFT); if (!allowed) - return -EPERM; + goto failed; + + err = -EFAULT; if (allowed == 2) { /* Show zeros for restricted memory. */ remaining = clear_user(buf, sz); @@ -149,24 +158,32 @@ static ssize_t read_mem(struct file *file, char __user *buf, */ ptr = xlate_dev_mem_ptr(p); if (!ptr) - return -EFAULT; - - remaining = copy_to_user(buf, ptr, sz); + goto failed; + err = probe_kernel_read(bounce, ptr, sz); unxlate_dev_mem_ptr(p, ptr); + if (err) + goto failed; + + remaining = copy_to_user(buf, bounce, sz); } if (remaining) - return -EFAULT; + goto failed; buf += sz; p += sz; count -= sz; read += sz; } + kfree(bounce); *ppos += read; return read; + +failed: + kfree(bounce); + return err; } static ssize_t write_mem(struct file *file, const char __user *buf, diff --git a/security/Kconfig b/security/Kconfig index e8e449444e65..3b4effd8bbc2 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -143,6 +143,7 @@ config HARDENED_USERCOPY bool "Harden memory copies between kernel and userspace" depends on HAVE_HARDENED_USERCOPY_ALLOCATOR select BUG + imply STRICT_DEVMEM help This option checks for obviously wrong memory regions when copying memory to/from the kernel (via copy_to_user() and -- cgit v1.2.3-55-g7522 From 74d83a5d747ad22f33927ffd2c77f237fda2e878 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 4 Jan 2018 16:45:39 -0600 Subject: char: xillybus: remove direct dependency on DT functions Drivers generally should not need to depend directly on OF_ADDRESS or OF_IRQ. Convert xillybus to use the preferred platform_get_resource() and platform_get_irq() functions to remove this dependency. Cc: Eli Billauer Signed-off-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/char/xillybus/Kconfig | 4 ++-- drivers/char/xillybus/xillybus_of.c | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/xillybus/Kconfig b/drivers/char/xillybus/Kconfig index b302684d86c1..a1f16df08d32 100644 --- a/drivers/char/xillybus/Kconfig +++ b/drivers/char/xillybus/Kconfig @@ -4,7 +4,7 @@ config XILLYBUS tristate "Xillybus generic FPGA interface" - depends on PCI || (OF_ADDRESS && OF_IRQ) + depends on PCI || OF select CRC32 help Xillybus is a generic interface for peripherals designed on @@ -24,7 +24,7 @@ config XILLYBUS_PCIE config XILLYBUS_OF tristate "Xillybus over Device Tree" - depends on OF_ADDRESS && OF_IRQ && HAS_DMA + depends on OF && HAS_DMA help Set to M if you want Xillybus to find its resources from the Open Firmware Flattened Device Tree. If the target is an embedded diff --git a/drivers/char/xillybus/xillybus_of.c b/drivers/char/xillybus/xillybus_of.c index 78a492f5acfb..4d6625ccb48f 100644 --- a/drivers/char/xillybus/xillybus_of.c +++ b/drivers/char/xillybus/xillybus_of.c @@ -15,10 +15,6 @@ #include #include #include -#include -#include -#include -#include #include #include "xillybus.h" @@ -123,7 +119,7 @@ static int xilly_drv_probe(struct platform_device *op) struct xilly_endpoint *endpoint; int rc; int irq; - struct resource res; + struct resource *res; struct xilly_endpoint_hardware *ephw = &of_hw; if (of_property_read_bool(dev->of_node, "dma-coherent")) @@ -136,13 +132,13 @@ static int xilly_drv_probe(struct platform_device *op) dev_set_drvdata(dev, endpoint); - rc = of_address_to_resource(dev->of_node, 0, &res); - endpoint->registers = devm_ioremap_resource(dev, &res); + res = platform_get_resource(op, IORESOURCE_MEM, 0); + endpoint->registers = devm_ioremap_resource(dev, res); if (IS_ERR(endpoint->registers)) return PTR_ERR(endpoint->registers); - irq = irq_of_parse_and_map(dev->of_node, 0); + irq = platform_get_irq(op, 0); rc = devm_request_irq(dev, irq, xillybus_isr, 0, xillyname, endpoint); -- cgit v1.2.3-55-g7522 From 9ff6576e124b1227c27c1da43fe5f8ee908263e0 Mon Sep 17 00:00:00 2001 From: Gustavo A. R. Silva Date: Tue, 23 Jan 2018 09:35:29 -0600 Subject: char: lp: use true or false for boolean values Assign true or false to boolean variables instead of an integer value. This issue was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Greg Kroah-Hartman --- drivers/char/lp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/lp.c b/drivers/char/lp.c index be14abf70da1..8c4dd1a3bb6a 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -900,7 +900,7 @@ static int __init lp_setup (char *str) printk(KERN_INFO "lp: too many ports, %s ignored.\n", str); } else if (!strcmp(str, "reset")) { - reset = 1; + reset = true; } return 1; } -- cgit v1.2.3-55-g7522