Segmentation fault

From Wikipedia, the free encyclopedia - View original article

 
Jump to: navigation, search

A segmentation fault (often shortened to segfault), bus error, or access violation is generally an attempt to access memory that the CPU cannot physically address. It occurs when the hardware notifies an operating system about a memory access violation. The OS kernel will in response usually perform some corrective action, such as sending the offending process a signal, terminating it, or causing it to dump core. In some circumstances it is possible for processes to request they be allowed to recover on their own, by installing a custom signal handler.[1]

Bus error[edit]

On POSIX-compliant platforms, bus errors usually result in the SIGBUS signal being sent to the process that caused the error. SIGBUS can also be caused by any general device fault that the computer detects. A bus error rarely means that the computer hardware is physically broken—it is normally caused by a bug in a program's source code.

There are at least three main causes of bus errors:

Non-existent address[edit]

Software instructs the CPU to read or write a specific physical memory address. Accordingly, the CPU sets this physical address on its address bus and requests all other hardware connected to the CPU to respond with the results, if they answer for this specific address. If no other hardware responds, the CPU raises an exception, stating that the requested physical address is unrecognized by the whole computer system. Note that this only covers physical memory addresses. Trying to access an undefined virtual memory address is generally considered to be a segmentation fault rather than a bus error, though if the MMU is separate, the processor can't tell the difference.

Unaligned access[edit]

Most CPUs are byte-addressable, where each unique memory address refers to an 8-bit byte. Most CPUs can access individual bytes from each memory address, but they generally cannot access larger units (16 bits, 32 bits, 64 bits and so on) without these units being "aligned" to a specific boundary (the x86 platform being a notable exception).

For example, if multi-byte accesses must be 16 bit-aligned, addresses (given in bytes) at 0, 2, 4, and so on would be considered aligned and therefore accessible, while addresses 1, 3, 5, and so on would be considered unaligned. Similarly, if multi-byte accesses must be 32-bit aligned, addresses 0, 4, 8, 12, and so on would be considered aligned and therefore accessible, and all addresses in between would be considered unaligned. Attempting to access a unit larger than a byte at an unaligned address can cause a bus error.

CPUs generally access data at the full width of their data bus at all times. To address bytes, they access memory at the full width of their data bus, then mask and shift to address the individual byte. Systems tolerate this inefficient algorithm, as it is an essential feature for most software, especially string processing. Unlike bytes, larger units can span two aligned addresses and would thus require more than one fetch on the data bus. It is possible for CPUs to support this, but this functionality is rarely required directly at the machine code level, thus CPU designers normally avoid implementing it and instead issue bus errors for unaligned memory access.

Paging errors[edit]

FreeBSD, Linux and Solaris can signal a bus error when virtual memory pages cannot be paged in, e.g. because it has disappeared (e.g. accessing a memory-mapped file or executing a binary image which has been truncated while the program was running),[2] or because a just-created memory mapped file cannot be physically allocated, because the disk is full.

Segmentation fault and access violation[edit]

Example of human generated signal

A segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (for example, attempting to write to a read-only location, or to overwrite part of the operating system).

"Segmentation" is a historical term for the approach to memory management nowadays known as paging (see e.g. Lions' Commentary on UNIX 6th Edition, with Source Code), but the term is still used in the context of a "segmentation fault" error; page fault has a different meaning (though a page fault can lead to a segmentation fault).

Different operating systems have different signal names to indicate that a segmentation fault has occurred. On Unix-like operating systems, a signal called SIGSEGV (abbreviated from segmentation violation) is sent to a process that accesses an invalid memory address. On Microsoft Windows, a process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception.

Common causes[edit]

The following are some typical causes of a segmentation fault:

Generally, segmentation faults occur because a pointer is NULL, or because it points to random memory (probably never initialized to anything), or because it points to memory that has been freed/deallocated/"deleted".

For example (in C++):

 char *p1 = NULL;           // Initialized to null, which is OK,                            // (but cannot be dereferenced on many systems). char *p2;                  // Not initialized at all. char *p3  = new char[20];  // Great! It's allocated, delete [] p3;              // but now it isn't anymore. 

Now, dereferencing any of these variables could cause a segmentation fault.

Handling[edit]

The default action for a segmentation fault or bus error is abnormal termination of the process that triggered it. A core file may be generated to aid debugging, and other platform-dependent actions may also be performed. For example, Linux systems using the grsecurity patch may log SIGSEGV signals in order to monitor for possible intrusion attempts using buffer overflows.

Examples[edit]

Bus error example[edit]

This is an example of unaligned memory access, written in the C programming language with AT&T assembly syntax.

 #include <stdlib.h>   int main(int argc, char **argv) {     int *iptr;     char *cptr;   #if defined(__GNUC__) # if defined(__i386__)     /* Enable Alignment Checking on x86 */     __asm__("pushf\norl $0x40000,(%esp)\npopf"); # elif defined(__x86_64__)       /* Enable Alignment Checking on x86_64 */     __asm__("pushf\norl $0x40000,(%rsp)\npopf"); # endif #endif       /* malloc() always provides aligned memory */     cptr = (char *) malloc(sizeof(int) + 1);       /* Increment the pointer by one, making it misaligned */     iptr = (int *) ++cptr;       /* Dereference it as an int pointer, causing an unaligned access */     *iptr = 42;       return 0; } 

Compiling and running the example on a POSIX compliant OS on x86 demonstrates the error:

  $ gcc -ansi sigbus.c -o sigbus  $ ./sigbus   Bus error  $ gdb ./sigbus  (gdb) r  Program received signal [[SIGBUS]], Bus error.  0x080483ba in main ()  (gdb) x/i $pc  0x80483ba <main+54>:    mov    DWORD PTR [eax],0x2a  (gdb) p/x $eax  $1 = 0x804a009  (gdb) p/t $eax & (sizeof(int) - 1)  $2 = 1 

The GDB debugger shows that the immediate value 0x2a is being stored at the location stored in the EAX register, using X86 assembly language. This is an example of register indirect addressing.

Printing the low order bits of the address shows that it is not aligned to a word boundary ("dword" using x86 terminology).

Segmentation fault example[edit]

Segmentation fault on an EMV keypad

Here is an example of ANSI C code that should create a segmentation fault on platforms with memory protection (note that modifying a string literal is undefined behavior according to the ANSI C standard):

  int main(void)  {      char *s = "hello world";      *s = 'H';  } 

When the program containing this code is compiled, the string "hello world" is placed in the section of the program executable file marked as read-only; when loaded, the operating system places it with other strings and constant data in a read-only segment of memory. When executed, a variable, s, is set to point to the string's location, and an attempt is made to write an H character through the variable into the memory, causing a segmentation fault. Compiling such a program with a compiler that does not check for the assignment of read-only locations at compile time, and running it on a Unix-like operating system produces the following runtime error:

  $ gcc segfault.c -g -o segfault  $ ./segfault  Segmentation fault 

Backtrace of the core file from gdb:

  Program received signal SIGSEGV, Segmentation fault.  0x1c0005c2 in main () at segfault.c:6  6               *s = 'H'; 

The conditions under which segmentation violations occur and how they manifest themselves are specific to an operating system.

Because a very common program error is a null pointer dereference (a read or write through a null pointer, used in C to mean "pointer to no object" and as an error indicator), most operating systems map the null pointer's address such that accessing it causes a segmentation fault.

  int *ptr = NULL;  *ptr = 1; 

This sample code creates a null pointer, and tries to assign a value to its non-existent target. Doing so causes a segmentation fault at runtime on many operating systems.

Another example is recursion without a base case:

  int main(void)  {     main();     return 0;  } 

which causes the stack to overflow which results in a segmentation fault.[3] Note that infinite recursion may not necessarily result in a stack overflow depending on the language, optimizations performed by the compiler and the exact structure of a code. In this case, the behavior of unreachable code (the return statement) is undefined, so the compiler can eliminate it and use a tail call optimization that might result in no stack usage. Other optimizations could include translating the recursion into iteration, which given the structure of the example function would result in the program running forever, while probably not overflowing its stack.

See also[edit]

References[edit]

External links[edit]