Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ae531c26 authored by Arjan van de Ven's avatar Arjan van de Ven Committed by Ingo Molnar
Browse files

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: default avatarArjan van de Ven <arjan@linux.intel.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 94bc891b
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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
+19 −0
Original line number Diff line number Diff line
@@ -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;
+20 −0
Original line number Diff line number Diff line
@@ -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;

+28 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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;