Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access mmaped /dev/mem without crashing the Linux kernel?

I have a simple program that tries to access the physical memory in user space, where the kernel stores the 1st struct page. On a 64 bit machine this address is:

  • kernel virtual address: ffffea0000000000
  • physical address: 0000620000000000

I am trying to access this physical address through mmap in user space. But the following code crashes the kernel.

int *addr;
if ((fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0 ) {
    printf("Error opening file. \n");
    close(fd);
    return (-1);
}
/* mmap.  address of first struct page for 64 bit architectures 
 * is 0x0000620000000000.
 */
addr = (int *)mmap(0, num*STRUCT_PAGE_SIZE, PROT_READ, MAP_PRIVATE,
            fd, 0x0000620000000000);
printf("addr: %p \n",addr);
printf("addr: %d \n",*addr); /* CRASH. */
like image 977
Vinay Avatar asked Aug 09 '12 21:08

Vinay


People also ask

What is MMAP Linux kernel?

MMAP is a UNIX system call that maps files into memory. It's a method used for memory-mapped file I/O. It brings in the optimization of lazy loading or demand paging such that the I/O or reading file doesn't happen when the memory allocation is done, but when the memory is accessed.

What is Dev MEM Linux?

/dev/mem is a character device file that is an image of the main memory of the computer. It may be used, for example, to examine (and even patch) the system. Byte addresses in /dev/mem are interpreted as physical memory addresses. References to nonexistent locations cause errors to be returned.


2 Answers

I think I've found the issue -- it's to do with /dev/mem memory mapping protection on the x86.

Pl refer to this LWN article: "x86: introduce /dev/mem restrictions with a config option" http://lwn.net/Articles/267427/

CONFIG_NONPROMISC_DEVMEM

Now (i tested this on a recent 3.2.21 kernel), the config option seems to be called CONFIG_STRICT_DEVMEM.

I changed my kernel config:

$ grep DEVMEM .config
# CONFIG_STRICT_DEVMEM is not set
$ 

When the above prg was run with the previous kernel, with CONFIG_STRICT_DEVMEM SET: dmesg shows:

[29537.565599] Program a.out tried to access /dev/mem between 1000000->1001000.
[29537.565663] a.out[13575]: segfault at ffffffff ip 080485bd sp bfb8d640 error 4 in a.out[8048000+1000]

This is because of the kernel protection..

When the kernel was rebuilt (with the CONFIG_STRICT_DEVMEM UNSET) and the above prg was run :

# ./a.out 
mmap failed: Invalid argument
# 

This is because the 'offset' parameter is > 1 MB (invalid on x86) (it was 16MB).

After making the mmap offset to be within 1 MB:

# ./a.out 
addr: 0xb7758000
*addr: 138293760 
# 

It works! See the above LWN article for details.

On x86 architectures with PAT support (Page Attribute Table), the kernel still prevents the mapping of DRAM regions. The reason for this as mentioned in the kernel source is:

This check is nedded to avoid cache aliasing when PAT is enabled

This check will cause a similar error to the one mentioned above. For example:

Program a.out tried to access /dev/mem between [mem 68200000-68201000].

This restriction can be removed by disabling PAT. PAT can be disabled by adding the "nopat" argument to the kernel command line at boot time.

like image 191
kaiwan Avatar answered Sep 28 '22 02:09

kaiwan


On x86 architectures with PAT support (Page Attribute Table), the kernel can prevent the mapping of DRAM regions (even if it is compiled without setting CONFIG_NONPROMISC_DEVMEM).

The reason for this as mentioned in the kernel source is:

This check is nedded to avoid cache aliasing when PAT is enabled

This check will cause a similar error to appear in dmesg as the one mentioned in kaiwan's answer above above. For example:

Program a.out tried to access /dev/mem between [mem 68200000-68201000].

This restriction can be removed by disabling PAT.

PAT can be disabled by adding the nopat argument to the kernel command-line at boot time.

like image 32
Safayet Ahmed Avatar answered Sep 28 '22 02:09

Safayet Ahmed