Why does setting a value at an arbitrary memory location not work?

前端 未结 3 1105
我在风中等你
我在风中等你 2021-01-17 08:05

I have this code:

#include 
#include 
#include 
#include 

int main (int argc, char** argv)          


        
相关标签:
3条回答
  • 2021-01-17 08:57

    What you are doing:

    *(volatile uint8_t*)0x12345678u = 1;
    int var = *(volatile uint8_t*)0x12345678;
    

    is totally wrong.

    You have no guarantee whatsoever that an arbitrary address like 0x12345678 will be accessible, not to mention writable by your program. In other words, you cannot set a value to an arbitrary address and expect it to work. It's undefined behavior to say the least, and will most likely crash your program due to the operating system stopping you from touching memory you don't own.

    The "command terminated" that you get when trying to run your program happens exactly because the operating system is preventing your program from accessing a memory location it is not allowed to access. Your program gets killed before it can do anything.


    If you are on Linux, you can use the mmap function to request a memory page at an (almost) arbitrary address before accessing it (see man mmap). Here's an example program which achieves what you want:

    #include <sys/mman.h>
    #include <stdio.h>
    
    #define WANTED_ADDRESS (void *)0x12345000
    #define WANTED_OFFSET 0x678 // 0x12345000 + 0x678 = 0x12345678
    
    int main(void) {
        // Request a memory page starting at 0x12345000 of 0x1000 (4096) bytes.
        unsigned char *mem = mmap(WANTED_ADDRESS, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    
        // Check if the OS correctly granted your program the requested page.
        if (mem != WANTED_ADDRESS) {
            perror("mmap failed");
            return 1;
        }
    
        // Get a pointer inside that page.
        int *ptr = (int *)(mem + WANTED_OFFSET); // 0x12345678
    
        // Write to it.
        *ptr = 123;
    
        // Inspect the results.
        printf("Value  : %d\n", *ptr);
        printf("Address: %p\n", ptr);
    
        return 0;
    }
    
    0 讨论(0)
  • 2021-01-17 08:59

    The quote from C11 standard 6.5.3.2p4:

    4 The unary * operator denotes indirection. [...] If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.

    You use * operator on (volatile uint8_t*)0x12345678u pointer. Is this a valid pointer? Is it invalid pointer? What is an "invalid value" of a pointer?

    There is no check that allows to find out which particilar pointer values are valid, which aren't. It is not implemented in C language. A random pointer may just happen to be a valid pointer. But most, most probably it is an invalid pointer. In which case - the behavior is undefined.

    Dereferencing an invalid pointer is undefined behavior. But - outside of C scope and into operating system - on *unix systems trying to access memory that you are not allowed to, should raise a signal SIGSEGV on your program and terminate your program. Most probably this is what happens. Your program is not allowed to access memory location that is behind 0x12345678 value, the operating system specifically protects against that.

    Also note, that systems use ASLR, so that pointer values within your program are indeed in some degree random. There are not linear, ie. *(char*)0x01 will not access the first byte in your ram. Operating system (or more exact, the underlying hardware as configured by the operating system) translates pointer values in your program to physical location in ram using what is called virtual memory. The same pointer values may just happen to be valid on the second run of your program. But most probably, because pointers can have so many values, most probably it isn't a valid pointer. Your operating system kills your program, as it detects an invalid memory access.

    0 讨论(0)
  • 2021-01-17 09:03

    The operating system and loader do not automatically make every possible address available to your program. The virtual address space of your process is constructed on demand by various operations of the program loader and of services inside the process. Although every address “exists” in the sense of being a potential address of memory, what happens when a process attempts to access an address is controlled by special data structures in the system. Those data structures control whether a process can read, write, or execute various portions of memory, whether the virtual addresses are currently mapped to physical memory, and whether the virtual addresses are not currently mapped to memory but will be provide with physical memory when needed. Initially, much of a process’ address space is marked not in use (or at least implicitly marked, in that none of the explicit records for the address space apply to it).

    In the executions of your program you have attempted so far, the address 0x12345678 has not been mapped and marked available to your process, so, when your process attempted to use it, the system detected a fault and terminated your process.

    (Some systems randomize the layout of the address space when a program is being loaded, to make it harder for an attacker to exploit bugs in a program. Because of this, it is possible that 0x12345678 will be accessible in some executions of your program and not others.)

    0 讨论(0)
提交回复
热议问题