Is it possible to write at virtual 0x0 on a classical OS?

262 Views Asked by At

I'm not sure if I'm asking the question right, but I'm wondering if it's possible for a C or ASM program to write at the virtual adress 0x0 ?

I know the kernel don't allow write/read at virtual 0x0, but I'm wondering if there's some strange way to do this. Using maybe a flag for ld or gcc that I don't know about ?

The settings are as follow :

  • Computer running Linux on x86-64
  • Can use root if necessary
4

There are 4 best solutions below

15
fuz On BEST ANSWER

Three steps are needed to do this:

  1. Configure the system to permit mapping memory at address zero. This is done by setting the vm.mmap_min_addr sysctl to 0:

    sysctl vm.mmap_min_addr=0
    

    If you are using Wine on the system, there's a chance this sysctl is already configured as it is needed for Wine to be able to execute 16 bit code.

    An alterative to vm.mmap_min_addr=0 is to run your process with CAP_SYS_RAWIO, such as by running as root like sudo strace ./a.out. (strace lets you see the system calls it makes, so you can see mmap return a successful 0 rather than (void*)-1)

  2. Establish a mapping at address 0. To let the kernel have you write data to address 0, you need to map some memory there. This is easily done by calling mmap to map some memory there or by arranging for your binary to load a segment there.

    For an mmap example, see allocating address zero on Linux with mmap fails

  3. Dereference a pointer to address 0. This can be done with C code like *(int *)0 = 42.
    This happens to compile to the asm we want with GCC or clang with optimization disabled1.


Footnote 1:
0 is a null pointer constant in C, and dereferencing it is undefined behaviour. With optimization disabled, GCC and clang just compile to asm that stores to that address (Godbolt) , but clang warns indirection of non-volatile null pointer will be deleted, not trap, consider using __builtin_trap() or qualifying pointer with 'volatile'. That is indeed what happens with optimization enabled. GCC -O2 or higher still emits a store instruction, but on x86-64 follows it with a ud2 illegal instruction trap.

volatile int *volatile zero_ptr = 0; *zero_ptr = 42; works in GCC and clang to hide the UB from the compiler: Godbolt. (Making the pointer object itself volatile hides the fact that it's a null pointer. Making it a pointer to volatile int works around a GCC bug(?) where it optimizes away the actual store, perhaps assuming that it can't be pointing to anything with a lifetime that outlasts this function.)

0
John Bollinger On

I'm not sure if I'm asking the question right, but I'm wondering if it's possible for a C or ASM program to write at the virtual adress 0x0 ?

There is no rule in the C language that forbids writing to 0x0 or any other particular address. I'm fairly certain that the same applies to most assembly languages.

In fact, C is not specified in terms of numeric addresses at all. Pointers are the C type category for addresses, and these are not numeric. C defines conversions between pointers and integers, but with the exception of null pointer constants (see below) it does not attribute any specific significance to them.

Although the C language has no concept of an address 0, the host system may very well have one. If it does (likely), and if there is a way to form a C pointer pointing to a modifiable object residing at that address (also likely), then there is good reason to think a C program could use such a pointer to modify the object to which it points, just as it would any other valid pointer. For example:

int *pointer_to_address_0 = ...;
*pointer_to_address_0 = 1;

The question may be premised in part on C null pointer constants, which have a form that could be mistaken as meaning a pointer to address 0, yet are expressly specified to not point to any object. For example, 0 or (void *) 0. But null pointer constants are a special syntactic construct, not an exercise of a more general language provision. Even if, without basis in the spec, we take conversions from integers to pointers as mapping numeric addresses to corresponding pointers, the spec does not say that converting the value 0 to a pointer yields a null pointer generally, nor that converting a null pointer to an integer necessarily yields 0.

The question may also be premised in part on the fact that some operating environments that feature flat, numerically-addressible memory spaces take steps to make address 0 inaccessible. Such efforts may be inspired by the observation that, at least on virtual memory systems, attempts to access very low addresses are usually erroneous. In conjunction with some forms of pointer representation, that can contribute to attempts to dereference null pointers failing immediately and loudly, which might not otherwise be the case. But that's not about the C language or assembly language, and even in environments exhibiting such behavior, there may be workarounds. Another answer presents such a workaround for Linux.

1
Luis Colorado On

The basic problem is that the kernel doesn't allow you to write at virtual address 0 because that page is not mapped, so it doesn't correspond to physical memory.

When you try to read or write (in machine instructions) to the address 0, there's a trap issued by the kernel because that address doesn't exist. That's not a requirement of the operating system. You could perfectly map memory at that address and use it as normal. It's the C runtime convention that considers that the NULL pointer should point nowhere, and so a page of memory is reserved at the bottom of the virtual address space to make the program to fail, if an access is made to it.

So, if you are going to run C programs, then it is better to follow the convention and get interrupted by the kernel to inform you that you tried to access the memory pointed by a NULL pointer.

Making that page inaccessible is the responsibility of the linker script that is read by the linker (ld(1)) with the "Memory map" of a process, and that reserves one page at address 0 to be unassigned by the kernel when loading the program.

0
user3344003 On

The reason you cannot access address zero in virtual memory systems is that linker do not instruct the loader to map that page by default in the executable file. The reason is to trap errors. Prior to virtual memory systems, you could easily access address zero and NULL pointers were valid.

There are two approaches to making address 0 valid. The specific mechanics depend upon your system.

  1. Instruct the linker to map address zero and make the first page WRITEABLE. Some linkers will allow you to do this. The method depends upon the linker.

  2. Map address zero using system services as writeable at run time. The process here depends upon the operating system user.