Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 416

kernel-2.6.18-238.el5.src.rpm

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);