¿Qué es un error de bus?

255

¿Qué significa el mensaje "error de bus" y en qué se diferencia de un segfault?

raldi
fuente
55
Me gustaría agregar una explicación simple para ambos: la falla de segmentación significa que está intentando acceder a la memoria que no tiene permitido (por ejemplo, no es parte de su programa). Sin embargo, en un error de bus generalmente significa que está intentando acceder a la memoria que no existe (por ejemplo, intenta acceder a una dirección a 12G pero solo tiene memoria de 8G) o si excede el límite de memoria utilizable.
xdevs23
¿En qué plataforma viste esto? ¿ORDENADOR PERSONAL? ¿Mac? x86? 32/64?
Peter Mortensen

Respuestas:

244

Los errores de bus son raros hoy en día en x86 y ocurren cuando su procesador ni siquiera puede intentar el acceso de memoria solicitado, típicamente:

  • utilizando una instrucción de procesador con una dirección que no cumple con sus requisitos de alineación.

Las fallas de segmentación ocurren cuando se accede a la memoria que no pertenece a su proceso, son muy comunes y generalmente son el resultado de:

  • usando un puntero a algo que fue desasignado.
  • utilizando un puntero no inicializado, por lo tanto, falso.
  • usando un puntero nulo.
  • desbordando un búfer.

PD: Para ser más precisos, esto no es manipular el puntero en sí mismo que causará problemas, es acceder a la memoria a la que apunta (desreferenciar).

bltxd
fuente
106
No son raros; Estoy en el Ejercicio 9 de Cómo aprender C de la manera difícil y ya
encontré
24
Otra causa de los errores del bus (en Linux de todos modos) es cuando el sistema operativo no puede hacer una copia de seguridad de una página virtual con memoria física (por ejemplo, condiciones de poca memoria o sin páginas grandes cuando se usa memoria de página enorme). Normalmente mmap (y malloc) solo reserve el espacio de dirección virtual, y el kernel asigna la memoria física a pedido (los llamados fallos de página programada). Haga un malloc lo suficientemente grande y luego escriba lo suficiente para obtener un error de bus.
Eloff
1
para mí la partición que contenía /var/cacheera simplemente askubuntu.com/a/915520/493379
c33s
2
En mi caso, un método static_casteditó un void *parámetro a un objeto que almacena una devolución de llamada (un atributo apunta al objeto y el otro al método). Entonces se llama la devolución de llamada. Sin embargo, lo que se pasó void *fue algo completamente diferente y, por lo tanto, la llamada al método causó el error del bus.
Christopher K.
@bltxd ¿Conoces la naturaleza de los errores del bus? es decir, el mensaje en el bus de anillo tiene algún mecanismo en el que una parada en el anillo también acepta un mensaje enviado por él pero a cualquier destino, ya que sugiere que ha dado la vuelta al anillo y no ha sido aceptado. Supongo que el búfer de relleno de línea devuelve un estado de error y cuando se retira, vacía la tubería y llama a la excepción correcta microroutine. Básicamente, esto requiere que el controlador de memoria acepte todas las direcciones en su rango, lo que sugeriría que cuando se cambian las BAR, etc., tendría que hacerlo internamente
Lewis Kelsey
84

Un defecto de seguridad es acceder a la memoria a la que no puede acceder. Es de solo lectura, no tienes permiso, etc.

Un error del bus está intentando acceder a la memoria que posiblemente no puede estar allí. Ha utilizado una dirección que no tiene sentido para el sistema, o el tipo de dirección incorrecto para esa operación.

Clinton Pierce
fuente
14

mmap Ejemplo mínimo de POSIX 7

El "error de bus" ocurre cuando el núcleo envía SIGBUSa un proceso.

Un ejemplo mínimo que lo produce porque ftruncatefue olvidado:

#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 para bytes len a páginas enteras 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 de memoria de pila no alineados en ARMv8 aarch64

Esto se mencionó en: ¿Qué es un error de bus? para SPARC, pero aquí proporcionaré un ejemplo más reproducible.

Todo lo que necesitas es un programa independiente aarch64:

.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

Ese programa genera SIGBUS en Ubuntu 18.04 aarch64, Linux kernel 4.15.0 en una máquina de 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.SAy SCTLR_EL1.SA0, he resumido los documentos relacionados un poco más aquí .

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
11

Creo que el kernel genera SIGBUS cuando una aplicación exhibe 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í

Oli
fuente
1
Depende de los trucos desagradables que estás haciendo con tu código. Puede desencadenar un error de BUS / Trampa de alineación si hace algo tonto como hacer cálculos de puntero y luego escribe para acceder a un modo problemático (es decir, configura una matriz uint8_t, agrega uno, dos o tres al puntero de la matriz y luego escribe a corto, int o largo e intenta acceder al resultado ofensivo.) Los sistemas X86 te permitirán hacer esto, aunque con una penalización de rendimiento real. ALGUNOS sistemas ARMv7 te permitirán hacer esto, pero la mayoría de los ARM, MIPS, Power, etc. te molestarán.
Svartalf
6

También puede obtener SIGBUS cuando una página de códigos no puede ser paginada por alguna razón.

Joshua
fuente
77
Esto sucede a menudo cuando actualizo el archivo .so mientras
ejecuto
Otra razón para hacerlo es si intenta mmapun archivo más grande que el tamaño de/dev/shm
ilija139
4

Una instancia clásica de un error de bus está en ciertas arquitecturas, como el SPARC (al menos algunos SPARC, tal vez esto ha cambiado), es cuando haces un acceso mal alineado. Por ejemplo:

unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;

Este fragmento intenta escribir el valor entero de 32 bits 0xdeadf00den una dirección que (muy probablemente) no esté alineada correctamente, y generará un error de bus en arquitecturas que son "exigentes" a este respecto. El procesador Intel x86 es, por cierto, no este tipo de arquitectura, que permitiría el acceso (aunque se ejecute más lentamente).

relajarse
fuente
1
En caso de que tuviera datos [8]; Ahora es un múltiplo de 4 en una arquitectura de 32 bits. Entonces, está alineado. ¿Seguiré recibiendo el error ahora? Además, explique, ¿es una mala idea una conversión de tipo de datos para punteros? ¿Causará errores de desalineación en una arquitectura frágil? Por favor explique, me ayudará.
diestro el
Je No es tanto la conversión de tipos como la conversión de tipos en un puntero en el que has hecho matemáticas de puntero. Mire cuidadosamente el código de arriba. El compilador ha alineado cuidadosamente con dword su puntero para obtener datos, y luego atornilla todo en el compilador al compensar la referencia por DOS y la conversión a una necesidad de tener acceso alineado con dword en lo que va a ser un límite que no sea dword.
Svartalf
"Frágil" no es la palabra que usaría para todo esto. Las máquinas y el código X86 han conseguido que las personas hagan cosas bastante tontas por un tiempo, siendo esta una de ellas. Vuelva a pensar su código si tiene este tipo de problema: para empezar, no es muy eficiente en X86.
Svartalf
@Svartalf: en x86, los accesos de palabras en punteros no alineados son ciertamente más lentos que los accesos de palabras a punteros alineados, pero al menos históricamente han sido más rápidos que el código simple que ensambla incondicionalmente las cosas a partir de bytes, y ciertamente son más simples que el código que intenta utilizar una combinación óptima de operaciones de varios tamaños. Desearía que el estándar C incluyera medios de empaquetar / desempacar tipos enteros más grandes a / desde una secuencia de enteros / caracteres más pequeños para permitir que el compilador use cualquier enfoque que sea mejor en una plataforma dada.
supercat
@Supercat: La cuestión es esta: te saldrás con la tuya en X86. Intenta esto en ARM, MIPS, Power, etc. y te van a pasar cosas desagradables. En ARM menor que Arch V7, su código tendrá una falla de alineación, y en V7, puede, SI su tiempo de ejecución está configurado para ello, manejarlo con un golpe de rendimiento GRAVE. Simplemente no quieres hacer esto. Son malas prácticas, para ser franco. : D
Svartalf
3

Un ejemplo específico de un error de bus que acabo de encontrar al programar C en OS X:

#include <string.h>
#include <stdio.h>

int main(void)
{
    char buffer[120];
    fgets(buffer, sizeof buffer, stdin);
    strcat("foo", buffer);
    return 0;
}

En caso de que no recuerde, los documentos strcatañaden el segundo argumento al primero cambiando el primer argumento (voltee los argumentos y funciona bien). En Linux, esto da un error de segmentación (como se esperaba), pero en OS X da un error de bus. ¿Por qué? Realmente no lo se.

Erik Vesteraas
fuente
Probablemente, la protección contra desbordamiento de pila genera un error de bus.
Joshua
1
"foo"se almacena en un segmento de memoria de solo lectura, por lo que es imposible escribir en él. No sería protección contra desbordamiento de pila, solo protección contra escritura de memoria (este es un agujero de seguridad si su programa puede reescribirse).
Mark Lakata
2

Depende de su sistema operativo, CPU, compilador y posiblemente otros factores.

En general, significa que el bus de la CPU no pudo completar un comando o sufrió un conflicto, pero eso podría significar una gran variedad de cosas dependiendo del entorno y el código que se ejecuta.

-Adán

Adam Davis
fuente
2

Normalmente significa un acceso no alineado.

Un intento de acceder a la memoria que no está físicamente presente también daría un error de bus, pero no verá esto si está utilizando un procesador con un MMU y un sistema operativo que no tiene errores, porque no tendrá -existente memoria asignada al espacio de direcciones de su proceso.

Mark Baker
fuente
2
Mi i7 ciertamente tiene una MMU, pero aún me encontré con este error mientras aprendía C en OS X (pasando el puntero no inicializado scanf). ¿Eso significa que OS X Mavericks tiene errores? ¿Cuál habría sido el comportamiento en un sistema operativo sin errores?
Calvin Huang
2

Estaba recibiendo un error de bus cuando el directorio raíz estaba al 100%.

goCards
fuente
1

Mi razón para el error del bus en Mac OS X fue que intenté asignar aproximadamente 1Mb en la pila. Esto funcionó bien en un subproceso, pero cuando se usa openMP esto conduce al error de bus, porque Mac OS X tiene un tamaño de pila muy limitado para subprocesos no principales .

Alleo
fuente
1

Estoy de acuerdo con todas las respuestas anteriores. Aquí están mis 2 centavos con respecto al error del BUS:

No es necesario que surja un error de BUS a partir de las instrucciones dentro del código del programa. Esto puede suceder cuando está ejecutando un binario y durante la ejecución, el binario se modifica (sobrescrito por una compilación o eliminado, etc.).

Verificar si este es el caso: una forma sencilla de verificar si esta es la causa es mediante la ejecución de instancias en ejecución del mismo binario y ejecutar una compilación. Ambas instancias en ejecución se bloquearían con un SIGBUSerror poco después de que la compilación haya finalizado y reemplazado el binario (el que ambas instancias están ejecutando actualmente)

Razón subyacente: Esto se debe a que el sistema operativo intercambia páginas de memoria y, en algunos casos, el binario podría no estar completamente cargado en la memoria y estos bloqueos se producirían cuando el sistema operativo intente recuperar la siguiente página del mismo binario, pero el binario ha cambiado desde la última vez Léelo.

Aditya Vikas Devarapalli
fuente
De acuerdo, esta es la causa más común de errores de bus en mi experiencia.
itaych
0

Para agregar a lo que blxtd respondió anteriormente, los errores del bus también ocurren cuando su proceso no puede intentar acceder a la memoria de una 'variable' particular .

for (j = 0; i < n; j++) {
    for (i =0; i < m; i++) {
        a[n+1][j] += a[i][j];
    }
}

Observe el uso ' inadvertido ' de la variable 'i' en el primer 'for loop'? Eso es lo que está causando el error del bus en este caso.

stuxnetting
fuente
Si m> = n, el bucle externo se ejecutará una vez o no, dependiendo del valor preexistente de i. Si m <n, se ejecutará indefinidamente con el índice j aumentando, hasta que se quede sin límites de su matriz y lo más probable es que cause un fallo de segmentación, no un error de bus. Si este código se compila, entonces no hay problema para acceder a la memoria de la variable 'i' misma. Lo siento, pero esta respuesta es incorrecta.
itaych
0

Me acabo de enterar por las malas que en un procesador ARMv7 puede escribir un código que le da un error de segmentación cuando no está optimizado, pero le da un error de bus cuando se compila con -O2 (optimice más).

Estoy usando el compilador cruzado GCC ARM gnueabihf de Ubuntu 64 bit.

oromoiluig
fuente
¿Cómo responde esto a la pregunta?
Peter Mortensen
-1

Un desbordamiento de búfer típico que produce un error de bus es,

{
    char buf[255];
    sprintf(buf,"%s:%s\n", ifname, message);
}

Aquí, si el tamaño de la cadena entre comillas dobles ("") es mayor que el tamaño de buf, da error de bus.

Vinaya Sagar
fuente
1
Je ... si este fuera el caso, tendrías problemas de errores de BUS en lugar de las vulnerabilidades de destrucción de la pila que lees todo el tiempo para Windows y otras máquinas. Los errores de BUS son causados ​​por un intento de acceder a la "memoria" a la que la máquina simplemente no puede acceder porque la dirección no es válida. (De ahí el término error "BUS"). Esto puede deberse a una serie de fallas, incluidas las alineaciones no válidas, y similares, siempre que el procesador no pueda colocar la dirección en las líneas del bus.
Svartalf