Bus errors are rarely seen on x86 systems. I investigated when a bus error (SIGBUS) occurs. I wrote x86 in the title, but I also checked SPARC.
Bus error (SIGBUS) is, as the name implies, an error that occurs on a bus. Occurs when an error occurs when the CPU makes an access request to physical memory (it is supposed to). Normally, what is visible to the user program is the logical address, and since the physical address is not directly touched, it is difficult for the user program to cause a Bus error. It is also known to occur due to alignment errors, but this does not happen on x86 because the alignment check is normally off. As far as I investigated, it was found that Bus error can be caused by two ways, "call a system call that causes SIGBUS" and "cause an alignment error after enabling alignment check".
An example from StackOverflow. SIGBUS appears when you touch outside the area secured by mmap.
bus.c
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
int main() {
FILE *f = tmpfile();
int *m = (int*)mmap(0, 4, PROT_WRITE, MAP_PRIVATE, fileno(f), 0);
*m = 0;
return 0;
}
$ gcc bus.c
$ ./a.out
[1] 16871 bus error ./bus
The man mmap also has a statement to that effect.
The system shall always zero-fill any partial page at the end of an object. Further, the system shall never write out any modified portions of the last page of an object which are beyond its end. References within the address range starting at pa and continuing for len bytes to whole pages following the end of an object shall result in delivery of a SIGBUS signal.
Also, mmap's Source explicitly returns VM_FAULT_SIGBUS.
An example found in here that is secured by char and accessed by int.
bus2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *p = (char*)malloc(sizeof(int) + 1);
memset(p, 0, sizeof(int) + 1);
p++;
printf("%d\n", *(int *)p);
return 0;
}
This ends normally despite the alignment inconsistency. This is because the x86 EFLAGS Alignment Check (AC) flag is turned off. If this is turned on, a Bus error will occur.
bus2p.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
asm( "pushf\n\torl $0x40000,(%rsp)\n\tpopf");
char *p = (char*)malloc(sizeof(int) + 1);
memset(p, 0, sizeof(int) + 1);
p++;
printf("%d\n", *(int *)p);
return 0;
}
Even if the AC flag is turned on, the bus error does not occur on Mac OS X and it ends normally.
Try to access the string area.
bus3.c
#include <stdio.h>
#include <stdlib.h>
int
main(void){
char *s = "test";
s[0] = '0';
printf("%s\n",s);
}
This results in a Segmentation Fault (SIGSEGV) on Linux, but gives a Bus error on Mac.
$ clang bus3.c
$ ./a.out
Bus error: 10
In summary, it looks like this. I also included the results of Cygwin. I also had a SPARC Linux system, so I checked it there as well (excluding bus2p.c, which uses inline assembly).
bus.c | bus2.c | bus2p.c | bus3.c | |
---|---|---|---|---|
Linux | SIGBUS | Successful completion | SIGBUS | SIGSEGV |
Mac OS X | SIGBUS | Successful completion | Successful completion | SIGBUS |
Cygwin | SIGBUS | Successful completion | Successful completion | SIGSEGV |
SPARC | SIGSEGV | SIGBUS | ---- | SIGSEGV |
In today's x86 system, Bus error doesn't seem to come out of the user program unless you explicitly enable alignment check to make alignment inconsistent access or use a system call that issues SIGBUS. The exception is Mac, which has been pointed out as the distinction between SIGBUS and SIGSEGV is ambiguous, and a bus error occurs just by accessing a character string. Even on the same Linux, if it is SPARC, the operation is quite different. I think it's true that SIGBUS is issued due to improper alignment, but I'm not sure why it is issued SIGSEGV due to improper mmap (Isn't it decided by POSIX?).
I don't like the fact that all the systems I checked have different behaviors.
Recommended Posts