Skip to content

Commit

Permalink
x86: introduce /dev/mem restrictions with a config option
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Arjan van de Ven authored and Ingo Molnar committed Apr 24, 2008
1 parent 94bc891 commit ae531c2
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 0 deletions.
12 changes: 12 additions & 0 deletions arch/x86/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions arch/x86/mm/init_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
20 changes: 20 additions & 0 deletions arch/x86/mm/init_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
28 changes: 28 additions & 0 deletions drivers/char/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions include/asm-x86/page.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit ae531c2

Please sign in to comment.