Is dd if=/dev/urandom of=/dev/mem safe?
Don't try this at home! It can crash your system, and if you're really unlucky it could damage a peripheral or make your computer unbootable.
Actually, on most platforms, it just fails with an error, but that depends on the hardware architecture. There is most definitely no guarantee that this is harmless unless you run the command as an unprivileged user. With an unprivileged user, the command is perfectly harmless because you can't open /dev/mem
.
When you run a command as root, you're supposed to know what you're doing. The kernel will sometimes prevent you from doing something dangerous, but not always. /dev/mem
is one of those potentially dangerous things where you're really supposed to know what you're doing.
I'm going to walk through how a write to /dev/mem
works on Linux. The general principle would be the same on other Unices, but things like kernel options are completely different.
What happens when a process reads or writes to a device file is up to the kernel. An access to a device file runs some code in the driver that handles this device file. For example, writing to /dev/mem
invokes the function write_mem
in drivers/char/mem.c
. This function takes 4 arguments: a data structure that represents the open file, a pointer to the data to write, the number of bytes to write, and the current position in the file.
Note that you only get that far if the caller had permission to open the file in the first place. Device files obey file permissions normally. The normal permissions of /dev/mem
are crw-r-----
owned by root:kmem
, so if you try to open it for writing without being root, you'll just get “permission denied” (EACCESS). But if you're root (or if root has changed the permissions of this file), the opening goes through and then you can attempt a write.
The code in the write_mem
function makes some sanity checks, but these checks aren't enough to protect against everything bad. The first thing it does is convert the current file position *ppos
to a physical address. If that fails (in practice, because you're on a platform with 32-bit physical addresses but 64-bit file offsets and the file offset is larger than 2^32), the write fails with EFBIG (file too large). The next check is whether the range of physical addresses to write is valid on this particular processor architecture, and there a failure results in EFAULT (bad address).
Next, on Sparc and m68k, any part of the write in the very first physical page is silently skipped.
We've now reached the main loop which iterates over the data in blocks that can fit within one MMU page.
/dev/mem
accesses physical memory, not virtual memory, but the processor instructions to load and store data in memory use virtual addresses, so the code needs to arrange to map the physical memory at some virtual address. On Linux, depending on the processor architecture and the kernel configuration, this mapping either exists permantently or has to be made on the fly; that's the job of xlate_dev_mem_ptr
(and unxlate_dev_mem_ptr
undoes whatever xlate_dev_mem_ptr
does). Then the function copy_from_user
reads from the buffer that was passed to the write
system call and just writes to the virtual address where the physical memory is currently mapped. The code emits normal memory store instructions, and what this means is up to the hardware.
Before I discuss that a write to a physical address does, I'll discuss a check that happens before this write. Inside the loop, the function page_is_allowed
blocks accesses to certain addresses if the kernel configuration option CONFIG_STRICT_DEVMEM
is enabled (which is the case by default): only addresses allowed by devmem_is_allowed
can be reached through /dev/mem
, for others the write fails with EPERM (operation not permitted). The description of this option states:
If this option is switched on, and IO_STRICT_DEVMEM=n, the /dev/mem file 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.
This is very x86-centric description. In fact, more generically, CONFIG_STRICT_DEVMEM
blocks access to physical memory addresses that map to RAM, but allows access to addresses that don't map to RAM. The details of what ranges of physical address are allowed depend on the processor architecture, but all of them exclude the RAM where data of the kernel and of user land processes is stored.
The additional option CONFIG_IO_STRICT_DEVMEM
(disabled as of Ubuntu 18.04) blocks accesses to physical addresses that are claimed by a driver.
Physical memory addresses that map to RAM. So there are physical memory addresses that don't map to RAM? Yes. That's the discussion I promised above about what it means to write to an address.
A memory store instruction does not necessarily write to RAM. The processor decomposes the address and decides which peripheral to dispatch the store to. (When I say “the processor”, I encompass peripheral controllers which may not come from the same manufacturer.) RAM is only one of those peripherals. How the dispatch is done is very dependent on the processor architecture, but the fundamentals are more or less the same on all architectures. The processor basically decomposes the higher bits of the address and looks them up in some tables that are populated based on hard-coded information, information obtained by probing some buses, and information configured by the software. A lot of caching and buffering may be involved, but in a nutshell, after this decomposition, the processor writes something (encoding both the target address and the data that's being stored) on some bus and then it's up to the peripheral to deal with it. (Or the outcome of the table lookup might be that there is no peripheral at this address, in which case the processor enters a trap state where it executes some code in the kernel that normally results in a SIGBUS for the calling process.)
A store to an address that maps to RAM doesn't “do” anything other than overwrite the value that was previously stored at this address, with the promise that a later load at the same address will give back the last stored value. But even RAM has a few addresses that don't behave this way: it has a few registers that can control things like refresh rate and voltage.
In general, a read or write to a hardware register does whatever the hardware is programmed to do. Most accesses to hardware work this way: the software (normally kernel code) accesses a certain physical address, this reaches the bus that connects the processor to the peripheral, and the peripheral does its thing. Some processors (in particular x86) also have separate CPU instructions that cause reads/writes to peripherals which are distinct from memory load and store, but even on x86, many peripherals are reached through load/store.
The command dd if=/dev/urandom of=/dev/mem
writes random data to whatever peripheral is mapped at address 0 (and subsequent addresses, as long as the writes succeed). In practice, I expect that on many architectures, physical address 0 doesn't have any peripheral mapped to it, or has RAM, and therefore the very first write attempt fails. But if there is a peripheral mapped at address 0, or if you change the command to write to a different address, you'll trigger something unpredictable in the peripheral. With random data at increasing addresses, it's unlikely to do something interesting, but in principle it could turn off the computer (there's probably an address that does this in fact), overwrite some BIOS setting that makes it impossible to boot, or even hit some buggy peripheral in a way that damages it.
alias Russian_roulette='dd if=/dev/urandom of=/dev/mem seek=$((4096*RANDOM+4096*32768*RANDOM))'
It is safe, if you have properly configured kernel ( safe because it won't work )
Per manual page mem(4):
/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.
So in theory, dd if=/dev/urandom of=/dev/mem
should overwrite whole address space of the physical memory you have installed, and since kernel and other programs run from memory this should effectively crash the system. In practice, there's limit. From the same man page:
Since Linux 2.6.26, and depending on the architecture, the CONFIG_STRICT_DEVMEM kernel configuration option limits the areas which can be accessed through this file.
Trying this on virtual machine Ubuntu 18.04 , it returns an error dd: writing to '/dev/mem': Operation not permitted
even with sudo
and despite permissions for root crw-r-----
. From Ubuntu Wiki:
/dev/mem protection
Some applications (Xorg) need direct access to the physical memory from user-space. The special file /dev/mem exists to provide this access. In the past, it was possible to view and change kernel memory from this file if an attacker had root access. The CONFIG_STRICT_DEVMEM kernel option was introduced to block non-device memory access (originally named CONFIG_NONPROMISC_DEVMEM).
So technically, no it is not safe (since it would crash the system) and if kernel option CONFIG_STRICT_DEVMEM
is disabled that's a security hole, but from what I see so far the command wouldn't run if that option is enabled. According to cross-site duplicate, a reboot will fix any issues with it, but of course data in RAM at that time would be lost and not flushed to disk (if any had to be).
There is a suggested method on the duplicate linked earlier using busybox devmem
so if you're determined to mess around with RAM, there may be a way after all.