x86: introduce /dev/mem restrictions with a config option

This patch introduces a restriction on /dev/mem: Only non-memory can be
read or written unless the newly introduced config option is set.

The X server needs access to /dev/mem for the PCI space, but it doesn't need
access to memory; both the file permissions and SELinux permissions of /dev/mem
just make X effectively super-super powerful. With the exception of the
BIOS area, there's just no valid app that uses /dev/mem on actual memory.
Other popular users of /dev/mem are rootkits and the like.
(note: mmap access of memory via /dev/mem was already not allowed since
a really long time)

People who want to use /dev/mem for kernel debugging can enable the config
option.

The restrictions of this patch have been in the Fedora and RHEL kernels for
at least 4 years without any problems.

Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
Arjan van de Ven 2008-04-24 23:40:47 +02:00 committed by Ingo Molnar
parent 94bc891b00
commit ae531c26c5
5 changed files with 80 additions and 0 deletions

View File

@ -5,6 +5,18 @@ config TRACE_IRQFLAGS_SUPPORT
source "lib/Kconfig.debug"
config NONPROMISC_DEVMEM
bool "Disable promiscuous /dev/mem"
default y
help
The /dev/mem file by default only allows userspace access to PCI
space and the BIOS code and data regions. This is sufficient for
dosemu and X and all common users of /dev/mem. With this config
option, you allow userspace access to all of memory, including
kernel and userspace memory. Accidental access to this is
obviously disasterous, but specific access can be used by people
debugging the kernel.
config EARLY_PRINTK
bool "Early printk" if EMBEDDED
default y

View File

@ -227,6 +227,25 @@ static inline int page_kills_ppro(unsigned long pagenr)
return 0;
}
/*
* devmem_is_allowed() checks to see if /dev/mem access to a certain address
* is valid. The argument is a physical page number.
*
*
* On x86, access has to be given to the first megabyte of ram because that area
* contains bios code and data regions used by X and dosemu and similar apps.
* Access has to be given to non-kernel-ram areas as well, these contain the PCI
* mmio resources as well as potential bios/acpi data regions.
*/
int devmem_is_allowed(unsigned long pagenr)
{
if (pagenr <= 256)
return 1;
if (!page_is_ram(pagenr))
return 1;
return 0;
}
#ifdef CONFIG_HIGHMEM
pte_t *kmap_pte;
pgprot_t kmap_prot;

View File

@ -664,6 +664,26 @@ EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
#endif /* CONFIG_MEMORY_HOTPLUG */
/*
* devmem_is_allowed() checks to see if /dev/mem access to a certain address
* is valid. The argument is a physical page number.
*
*
* On x86, access has to be given to the first megabyte of ram because that area
* contains bios code and data regions used by X and dosemu and similar apps.
* Access has to be given to non-kernel-ram areas as well, these contain the PCI
* mmio resources as well as potential bios/acpi data regions.
*/
int devmem_is_allowed(unsigned long pagenr)
{
if (pagenr <= 256)
return 1;
if (!page_is_ram(pagenr))
return 1;
return 0;
}
static struct kcore_list kcore_mem, kcore_vmalloc, kcore_kernel,
kcore_modules, kcore_vsyscall;

View File

@ -108,6 +108,30 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
}
#endif
#ifdef CONFIG_NONPROMISC_DEVMEM
static inline int range_is_allowed(unsigned long from, unsigned long to)
{
unsigned long cursor;
cursor = from >> PAGE_SHIFT;
while ((cursor << PAGE_SHIFT) < to) {
if (!devmem_is_allowed(cursor)) {
printk(KERN_INFO "Program %s tried to read /dev/mem "
"between %lx->%lx.\n",
current->comm, from, to);
return 0;
}
cursor++;
}
return 1;
}
#else
static inline int range_is_allowed(unsigned long from, unsigned long to)
{
return 1;
}
#endif
/*
* This funcion reads the *physical* memory. The f_pos points directly to the
* memory location.
@ -157,6 +181,8 @@ static ssize_t read_mem(struct file * file, char __user * buf,
*/
ptr = xlate_dev_mem_ptr(p);
if (!range_is_allowed(p, p+count))
return -EPERM;
if (copy_to_user(buf, ptr, sz))
return -EFAULT;
buf += sz;
@ -214,6 +240,8 @@ static ssize_t write_mem(struct file * file, const char __user * buf,
*/
ptr = xlate_dev_mem_ptr(p);
if (!range_is_allowed(p, p+sz))
return -EPERM;
copied = copy_from_user(ptr, buf, sz);
if (copied) {
written += sz - copied;

View File

@ -47,6 +47,7 @@
#ifndef __ASSEMBLY__
extern int page_is_ram(unsigned long pagenr);
extern int devmem_is_allowed(unsigned long pagenr);
extern unsigned long max_pfn_mapped;