¿Qué es un error de autobús? ¿Es diferente de una falla de segmentación?
¿Qué significa el mensaje "error de bus" y en qué se diferencia de un error de segmentación ?
Los errores de bus son raros hoy en día en x86 y ocurren cuando su procesador ni siquiera puede intentar el acceso a la memoria solicitado, generalmente:
- utilizando una instrucción de procesador con una dirección que no satisface sus requisitos de alineación.
Las fallas de segmentación ocurren al acceder a una memoria que no pertenece a su proceso. Son muy comunes y suelen ser el resultado de:
- usando un puntero a algo que fue desasignado.
- usando un puntero no inicializado y por lo tanto falso.
- usando un puntero nulo.
- desbordando un buffer.
PD: Para ser más precisos, no es la manipulación del puntero lo que causará problemas. Está accediendo a la memoria a la que apunta (desreferenciación).
Un error de segmentación es acceder a una memoria a la que no tienes permitido acceder. Es de solo lectura, no tienes permiso, etc...
Un error de bus intenta acceder a una memoria que no es posible que esté allí. Ha utilizado una dirección que no tiene sentido para el sistema o el tipo de dirección incorrecto para esa operación.
mmap
ejemplo mínimo de POSIX 7
El "error de bus" ocurre cuando el kernel envía SIGBUS
a un proceso.
Un mínimo ejemplo que lo produce porque ftruncate
quedó en el olvido:
#include <fcntl.h> /* O_ constants */
#include <unistd.h> /* ftruncate */
#include <sys/mman.h> /* mmap */
int main() {
int fd;
int *map;
int size = sizeof(int);
char *name = "/a";
shm_unlink(name);
fd = shm_open(name, O_RDWR | O_CREAT, (mode_t)0600);
/* THIS is the cause of the problem. */
/*ftruncate(fd, size);*/
map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
/* This is what generates the SIGBUS. */
*map = 0;
}
Corre con:
gcc -std=c99 main.c -lrt
./a.out
Probado en Ubuntu 14.04.
POSIX describe SIGBUS
como:
Acceso a una porción indefinida de un objeto de memoria.
La especificación mmap dice que:
Las referencias dentro del rango de direcciones que comienzan en pa y continúan por len bytes hasta páginas completas después del final de un objeto darán como resultado la entrega de una señal SIGBUS.
Y shm_open
dice que genera objetos de tamaño 0:
El objeto de memoria compartida tiene un tamaño de cero.
Entonces *map = 0
estamos tocando más allá del final del objeto asignado.
Accesos a memoria de pila no alineados en ARMv8 aarch64
Esto se mencionó en: ¿ Qué es un error de autobús? para SPARC, pero aquí proporcionaré un ejemplo más reproducible.
Todo lo que necesitas es un programa aarch64 independiente:
.global _start
_start:
asm_main_after_prologue:
/* misalign the stack out of 16-bit boundary */
add sp, sp, #-4
/* access the stack */
ldr w0, [sp]
/* exit syscall in case SIGBUS does not happen */
mov x0, 0
mov x8, 93
svc 0
Luego, ese programa genera SIGBUS en Ubuntu 18.04 aarch64, kernel de Linux 4.15.0 en una máquina servidor ThunderX2 .
Desafortunadamente, no puedo reproducirlo en el modo de usuario QEMU v4.0.0, no estoy seguro de por qué.
La falla parece ser opcional y controlada por los campos SCTLR_ELx.SA
y SCTLR_EL1.SA0
. He resumido los documentos relacionados un poco más aquí .
Creo que el kernel genera SIGBUS cuando una aplicación muestra una desalineación de datos en el bus de datos. Creo que dado que la mayoría [?] de los compiladores modernos para la mayoría de los procesadores rellenan/alinean los datos para los programadores, los problemas de alineación de antaño (al menos) se mitigaron y, por lo tanto, uno no ve SIGBUS con demasiada frecuencia en estos días (AFAIK).
De aquí