LINUX.ORG.RU
ФорумTalks

Всё, что вы хотели знать про SIGBUS, но так и не узнали


0

1

Есть миф о том, что, якобы, SIGBUS происходит только в одном случае - Invalid address alignment - и что, поскольку, физически x86 может обрабатывать такие случаи на x86 SIGBUS невозможен впринципе. Развенчаем этот миф.

glibc-2.24/sysdeps/unix/sysv/linux/x86/bits/siginfo.h знает целый ряд случаев в которых происходит SIGBUS:

  BUS_ADRALN = 1,               /* Invalid address alignment.  */
  BUS_ADRERR,                   /* Non-existant physical address.  */
  BUS_OBJERR,                   /* Object specific hardware error.  */
  BUS_MCEERR_AR,                /* Hardware memory error: action required.  */
  BUS_MCEERR_AO                 /* Hardware memory error: action optional.  */
Т.е, достаточно, например, случая обращения по Non-existant physical address чтобы произошёл SIGBUS.

Идём дальше. В ядре есть ловушки (traps) для отлова ошибок, которые, сюрприз, работают и на x86:

> grep -E "SIGBUS" linux-4.7/arch/x86/kernel/* -R
linux-4.7/arch/x86/kernel/cpu/mcheck/mce.c:      * 1: panic or SIGBUS on uncorrected errors, log corrected errors
linux-4.7/arch/x86/kernel/cpu/mcheck/mce.c:      * 2: SIGBUS or log uncorrected errors (if possible), log corr. errors
linux-4.7/arch/x86/kernel/cpu/mcheck/mce.c:      * 3: never panic or SIGBUS, log all errors (for testing only)
linux-4.7/arch/x86/kernel/cpu/mcheck/mce.c:                     force_sig(SIGBUS, current);
linux-4.7/arch/x86/kernel/traps.c:DO_ERROR(X86_TRAP_NP,     SIGBUS,  "segment not present",     segment_not_present)
linux-4.7/arch/x86/kernel/traps.c:DO_ERROR(X86_TRAP_SS,     SIGBUS,  "stack segment",           stack_segment)
linux-4.7/arch/x86/kernel/traps.c:DO_ERROR(X86_TRAP_AC,     SIGBUS,  "alignment check",         alignment_check)
Таким образом, возникновение SIGBUS совсем не означает того, что, якобы, или железо что-то внезапно не смогло обработать, или это глюк софта. Отнюдь. Современные реализации софта предоставляют удобные средства для дебага, в т.ч. и дебага использования памяти. В т.ч. и на уровне ядра. Таким образом, на кривом коде SIGBUS сегодня является нормой в т.ч. и на x86.

★★★★★
Ответ на: комментарий от Harald

В интернете есть примеры. Один из них:

#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 = 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;

    /*
       Following accesses will also result in sigbus error.
       short *sptr;
       int    i;

       sptr = (short *)&i;
       // For all odd value increments, it will result in sigbus.
       sptr = (short *)(((char *)sptr) + 1);
       *sptr = 100;

    */

    return 0;
}

saahriktu ★★★★★
() автор топика
Ответ на: комментарий от saahriktu

Какие-то у вас сложные примеры.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

int main (int argc, char** argv) {
       	int fd = open("test", O_WRONLY | O_CREAT, 0644);
       	write(fd, "abcd\n", 5);
       	close(fd);

       	fd = open("test", O_RDONLY);
       	char* ptr = (char*)mmap(0, 102400, PROT_READ, MAP_SHARED, fd, 0);
       	char z = ptr[4097];
       	write(1, &z, 1);
       	return 0;
}
$ gcc t.c && ./a.out
Bus error (core dumped)

Arrest
()

Я раньше думал, что обращение по несуществующему адресу - это всегда segfault, а тут вон оно чо

slowpony ★★★★★
()
Ответ на: комментарий от slowpony

речь идет о несуществующем физическом адресе

cvs-255 ★★★★★
()
Ответ на: комментарий от slowpony

Там интересно. Если процессор обращается по адресу, которого нет в MMU, происходит SIGBUS. Ядро перехватывает этот сигнал, и если есть этот адрес в свопе, размещает его в ОЗУ, если нет то SEGFAULT. (возможно есть и другие варианты, кроме сегфолта).

ymuv ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.