segfault.md (3284B)
1 # Segfault 2 3 A segmentation fault (segfault) is a runtime error that occurs when a program 4 tries to access a memory location it is not permitted to access — such as 5 reading or writing outside its allocated memory bounds. The operating system 6 catches this illegal access and terminates the offending process, sending a 7 SIGSEGV signal on Unix-like systems or raising a STATUS_ACCESS_VIOLATION 8 exception on Windows. 9 10 ## How It Happens 11 12 Program memory is divided into segments — text (instructions), data (global 13 variables), stack (local variables), and heap (dynamically allocated memory). A 14 segfault occurs when a reference falls outside the segment where a variable 15 resides, or when a write is attempted to a read-only segment. The most common 16 causes are: 17 18 - Null pointer dereference — accessing memory at address 0x0 19 20 - Buffer overflow — reading/writing past the end of an array 21 22 - Dangling pointer — using a pointer to memory that has already been freed 23 24 - Stack overflow — infinite recursion exhausting the stack 25 26 ## Rust and Segfaults 27 28 Rust's ownership and borrow checker system is specifically designed to eliminate 29 segfaults in safe code at compile time. According to the Rust team, a Rust 30 program can only segfault in two scenarios: you used unsafe code that violates 31 memory safety guarantees, or the Rust compiler itself has a bug. 32 33 ### Triggering a segfault with unsafe Rust 34 35 The most direct way is dereferencing a raw null or invalid pointer inside an 36 unsafe block: 37 38 ```rust 39 fn main() { 40 // Dereference an invalid memory address — instant segfault 41 unsafe { *(0x1 as *mut i32) = 1 }; 42 } 43 ``` 44 45 This writes to memory address 0x1, which is not mapped, causing the OS to send 46 SIGSEGV. 47 48 ### Stack overflow via infinite recursion 49 50 ```rust 51 fn recurse() { 52 recurse(); // infinite recursion → stack overflow → crash 53 } 54 55 fn main() { 56 recurse(); 57 } 58 ``` 59 60 Rust's runtime catches this and typically raises SIGABRT with a "thread has 61 overflowed its stack" message rather than a raw SIGSEGV, but it is the same 62 underlying mechanism. 63 64 ### Writing to read-only memory via unsafe 65 66 ```rust 67 fn main() { 68 let x: &str = "hello"; // stored in read-only memory 69 let ptr = x.as_ptr() as *mut u8; 70 unsafe { 71 *ptr = b'H'; // writing to read-only segment → segfault 72 } 73 } 74 ``` 75 76 ## Safe Rust vs. Unsafe Rust 77 78 | Scenario | Safe Rust | `unsafe` Rust | 79 | :----------------------- | :------------------------------------ | :----------------------------------- | 80 | Null pointer dereference | Impossible — `Option<T>` used instead | Possible with raw pointers | 81 | Buffer overflow | Panics with bounds check | Possible with raw pointer arithmetic | 82 | Dangling pointer | Prevented by borrow checker | Possible | 83 | Stack overflow | Handled gracefully (abort) | Same behaviour | 84 85 The key takeaway is that safe Rust **prevents segfaults by design** — the borrow 86 checker enforces memory safety rules at compile time that languages like C/C++ 87 leave to the programmer. When you do need low-level control, `unsafe` blocks opt 88 out of these guarantees and reintroduce the risk.