From: Eugene Teo <eteo@redhat.com> Date: Tue, 2 Sep 2008 22:50:17 +0800 Subject: [char] add range_is_allowed check to mmap_mem Message-id: 20080902145017.GA25046@kernel.sg O-Subject: [RHEL5.3 patch] BZ#460857 add range_is_allowed() check to mmap_mem() [v2] Bugzilla: 460857 RH-Acked-by: Michal Schmidt <mschmidt@redhat.com> This is for bz#460857. range_is_allowed() check is only added in {read,write}_mem(). It is possible for an illegitimate application to bypass these checks, and access /dev/mem beyond the 1M limit by calling mmap_mem() instead. Also changed the parameters of range_is_allowed() to pfn and size to handle more than 32-bits of physical address on 32-bit arch cleanly. Backport of upstream commit: e2beb3eae627211b67e456c53f946cede2ac10d7 Brew build: http://brewweb.devel.redhat.com/brew/taskinfo?taskID=1450108 Test status: Booted on i686. Tested with private phalanx2 rootkit. Signed-off-by: Eugene Teo <eteo@redhat.com> diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6f9c6cc..2451273 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -34,18 +34,20 @@ # include <linux/efi.h> #endif -static inline int range_is_allowed(unsigned long from, unsigned long to) +static inline int range_is_allowed(unsigned long pfn, unsigned long size) { - unsigned long cursor; + u64 from = ((u64)pfn) << PAGE_SHIFT; + u64 to = from + size; + u64 cursor = from; - cursor = from >> PAGE_SHIFT; - while ((cursor << PAGE_SHIFT) < to) { - if (!devmem_is_allowed(cursor)) { - printk ("Program %s tried to read /dev/mem between %lx->%lx.\n", + while (cursor < to) { + if (!devmem_is_allowed(pfn)) { + printk ("Program %s tried to read /dev/mem between %Lx->%Lx.\n", current->comm, from, to); return 0; } - cursor++; + cursor += PAGE_SIZE; + pfn++; } return 1; } @@ -167,7 +169,7 @@ static ssize_t read_mem(struct file * file, char __user * buf, */ ptr = xlate_dev_mem_ptr(p); - if (!range_is_allowed(p, p+count)) + if (!range_is_allowed(p >> PAGE_SHIFT, count)) return -EPERM; if (copy_to_user(buf, ptr, sz)) return -EFAULT; @@ -226,7 +228,7 @@ static ssize_t write_mem(struct file * file, const char __user * buf, */ ptr = xlate_dev_mem_ptr(p); - if (!range_is_allowed(ptr, ptr+sz)) + if (!range_is_allowed(p >> PAGE_SHIFT, sz)) return -EPERM; copied = copy_from_user(ptr, buf, sz); if (copied) { @@ -267,6 +269,9 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma) if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) return -EINVAL; + if (!range_is_allowed(vma->vm_pgoff, size)) + return -EPERM; + vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, size, vma->vm_page_prot);