Che cos'è un errore del bus?


255

Cosa significa il messaggio "errore bus" e in cosa differisce da un segfault?


5
Vorrei aggiungere una semplice spiegazione per entrambi: un errore di segmentazione significa che stai tentando di accedere alla memoria che non ti è permesso (ad esempio, non fa parte del tuo programma). Tuttavia, in caso di errore del bus, in genere significa che si sta tentando di accedere alla memoria che non esiste (ad es. Si tenta di accedere a un indirizzo a 12 G ma si dispone solo della memoria 8 G) o se si supera il limite della memoria utilizzabile.
xdevs23,

Su quale piattaforma hai visto questo? PC? Mac? x86? 32/64?
Peter Mortensen,

Risposte:


244

Gli errori di bus sono rari al giorno d'oggi su x86 e si verificano quando il processore non può nemmeno tentare l'accesso alla memoria richiesto, in genere:

  • utilizzando un'istruzione del processore con un indirizzo che non soddisfa i suoi requisiti di allineamento.

Gli errori di segmentazione si verificano quando si accede alla memoria che non appartiene al processo, sono molto comuni e sono in genere il risultato di:

  • usando un puntatore a qualcosa che è stato deallocato.
  • usando un puntatore falso quindi non inizializzato.
  • usando un puntatore nullo.
  • traboccando di un buffer.

PS: Per essere più precisi, questo non sta manipolando il puntatore stesso che causerà problemi, ma accedendo alla memoria a cui punta (dereferenziazione).


106
Non sono rari; Sono solo all'esercizio 9 di How to Learn C in the Hard Way e ne ho già incontrato uno ...
11684

24
Un'altra causa di errori del bus (su Linux comunque) è quando il sistema operativo non è in grado di eseguire il backup di una pagina virtuale con memoria fisica (ad esempio condizioni di memoria insufficiente o esaurimento di pagine enormi quando si utilizza una memoria di pagine enorme.) In genere mmap (e malloc) riservare lo spazio degli indirizzi virtuali e il kernel assegna la memoria fisica su richiesta (i cosiddetti errori di pagina morbida). Effettuare un malloc abbastanza grande, quindi scrivere su di esso abbastanza e si otterrà un errore del bus.
Eloff,

1
per me la partizione contenente /var/cacheera semplicemente askubuntu.com/a/915520/493379
c33

2
Nel mio caso, un metodo ha modificato static_castun void *parametro su un oggetto che memorizza un callback (un attributo punta all'oggetto e l'altro al metodo). Quindi viene chiamato il callback. Tuttavia, ciò che è stato passato void *era qualcosa di completamente diverso e quindi la chiamata del metodo ha causato l'errore del bus.
Christopher K.

@bltxd Conosci la natura degli errori del bus. cioè il messaggio sul ring bus ha qualche meccanismo in cui una fermata sul ring accetta anche un messaggio che è stato inviato da esso ma a qualunque destinazione in quanto suggerisce che è andato tutto intorno al ring e non è stato accettato. Immagino che il buffer di riempimento riga restituisca uno stato di errore e quando si ritira svuota la pipeline e chiama la microroutine di eccezione corretta. Ciò richiede fondamentalmente che il controller di memoria accetti tutti gli indirizzi nel suo intervallo, il che suggerirebbe che quando i BAR ecc. Vengono cambiati, dovrebbe essere internamente
Lewis Kelsey

84

Un segfault sta accedendo alla memoria a cui non ti è permesso accedere. È di sola lettura, non hai il permesso, ecc ...

Un errore del bus sta tentando di accedere alla memoria che non può essere presente. Hai utilizzato un indirizzo privo di significato per il sistema o un tipo di indirizzo errato per tale operazione.


14

mmap esempio di POSIX 7 minimo

"Errore bus" si verifica quando il kernel invia SIGBUSa un processo.

Un esempio minimo che lo produce perché è ftruncatestato dimenticato:

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

Corri con:

gcc -std=c99 main.c -lrt
./a.out

Testato su Ubuntu 14.04.

POSIX descrive SIGBUS come:

Accesso a una parte indefinita di un oggetto memoria.

Le specifiche mmap dicono che:

I riferimenti all'interno dell'intervallo di indirizzi che iniziano con pa e continuano per len byte a pagine intere dopo la fine di un oggetto devono generare un segnale SIGBUS.

E shm_open dice che genera oggetti di dimensione 0:

L'oggetto memoria condivisa ha una dimensione pari a zero.

Quindi, *map = 0stiamo toccando oltre la fine dell'oggetto allocato.

Accessi di memoria stack non allineati in ARMv8 aarch64

Questo è stato menzionato in: Cos'è un errore del bus? per SPARC, ma qui fornirò un esempio più riproducibile.

Tutto ciò che serve è un programma aarch64 indipendente:

.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

Quel programma genera quindi SIGBUS su Ubuntu 18.04 aarch64, kernel Linux 4.15.0 in una macchina server ThunderX2 .

Sfortunatamente, non riesco a riprodurlo in modalità utente QEMU v4.0.0, non sono sicuro del perché.

L'errore sembra essere facoltativo e controllato dai campi SCTLR_ELx.SAe SCTLR_EL1.SA0, ho riassunto un po 'più avanti i relativi documenti .


11

Credo che il kernel sollevi SIGBUS quando un'applicazione mostra un disallineamento dei dati sul bus dati. Penso che dal momento che la maggior parte dei [?] Compilatori moderni per la maggior parte dei processori pad / allinea i dati per i programmatori, i problemi di allineamento del passato (almeno) mitigati, e quindi non si vede SIGBUS troppo spesso in questi giorni (AFAIK).

Da: qui


1
Dipende dai cattivi trucchi che stai facendo con il tuo codice. È possibile attivare un errore BUS / Trappola di allineamento se si fa qualcosa di stupido come fare matematica puntatore e quindi digitare per accedere a una modalità problema (ad esempio, impostare un array uint8_t, aggiungere uno, due o tre al puntatore dell'array e quindi digitare in breve, int o long e prova ad accedere al risultato offensivo.) I sistemi X86 ti permetteranno praticamente di farlo, anche se con una vera penalità prestazionale. ALCUNI sistemi ARMv7 ti permetteranno di farlo, ma la maggior parte di ARM, MIPS, Power, ecc. Ti lasceranno a bocca aperta.
Svartalf,

6

Puoi anche ottenere SIGBUS quando non è possibile effettuare il paging di una tabella codici per qualche motivo.


7
Questo accade spesso quando aggiorno il file .so mentre
eseguo

Un altro motivo per succedere è se provi a mmapun file più grande della dimensione di/dev/shm
ilija139

3

Un esempio specifico di un errore del bus che ho appena riscontrato durante la programmazione di C su OS X:

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

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

Nel caso in cui non ricordi che i documenti strcataggiungono il secondo argomento al primo modificando il primo argomento (capovolgi gli argomenti e funziona benissimo). Su Linux questo dà un errore di segmentazione (come previsto), ma su OS X dà un errore del bus. Perché? Davvero non lo so.


Probabilmente la protezione da overflow dello stack genera un errore del bus.
Giosuè,

1
"foo"è memorizzato in un segmento di memoria di sola lettura, quindi è impossibile scriverlo. Non si tratterebbe di una protezione da overflow dello stack, ma solo di protezione da scrittura della memoria (questo è un buco di sicurezza se il tuo programma può riscrivere se stesso).
Mark Lakata,

3

Un'istanza classica di un errore del bus è su alcune architetture, come lo SPARC (almeno alcuni SPARC, forse questo è stato modificato), è quando si fa un accesso errato. Per esempio:

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

Questo frammento tenta di scrivere il valore intero a 32 bit 0xdeadf00dsu un indirizzo (molto probabilmente) non correttamente allineato e genererà un errore del bus su architetture "difficili" in questo senso. A proposito, Intel x86 non è una tale architettura, consentirebbe l'accesso (anche se eseguirlo più lentamente).


1
Nel caso, avessi i dati [8]; Questo è ora un multiplo di 4 in un'architettura a 32 bit. Quindi, è allineato. Riceverò ancora l'errore adesso? Inoltre, spiega, è una cattiva idea una conversione del tipo di dati per i puntatori. Provocherà errori di disallineamento su un'architettura fragile? Per favore, elaborami, mi aiuterà.
abile

Eh. Non è tanto la conversione del tipo quanto la conversione del tipo su un puntatore su cui hai eseguito la matematica del puntatore. Guarda attentamente il codice sopra. Il compilatore ha accuratamente allineato il tuo puntatore a dword per i dati, e poi rovini tutto sul compilatore, spostando il riferimento da DUE e digitando su un accesso estremamente allineato a quello che sarà un confine non-parola.
Svartalf,

"Fragile" non è la parola che userei per tutto questo. Le macchine e il codice X86 hanno indotto le persone a fare cose piuttosto stupide da un po 'di tempo, essendo questa una di queste. Ripensare il codice se si riscontra questo tipo di problema: all'inizio non è molto performante su X86.
Svartalf,

@Svartalf: su x86, gli accessi alle parole sui puntatori non allineati sono certamente più lenti degli accessi alle parole ai puntatori allineati, ma almeno storicamente sono stati più veloci del semplice codice che assembla incondizionatamente le cose dai byte e sono certamente più semplici del codice che prova utilizzare una combinazione ottimale di operazioni di varie dimensioni. Vorrei che lo standard C includesse i mezzi per impacchettare / decomprimere i tipi di numeri interi più grandi da / verso una sequenza di numeri / numeri interi più piccoli in modo da consentire al compilatore di utilizzare qualunque approccio sia meglio su una determinata piattaforma.
supercat,

@Supercat: Il fatto è questo: te ne vai via su X86. Provi questo su ARM, MIPS, Power, ecc. E ti accadranno cose brutte. Su ARM meno di Arch V7, il tuo codice avrà un errore di allineamento e su V7, puoi, SE il tuo runtime è impostato su di esso, gestirlo con un ENVOLTO hit di prestazione. Semplicemente non vuoi farlo. Sono cattive pratiche, per essere schietti. : D
Svartalf,

2

Dipende dal tuo sistema operativo, CPU, compilatore e forse altri fattori.

In generale, significa che il bus della CPU non è stato in grado di completare un comando o ha subito un conflitto, ma ciò potrebbe significare un'intera gamma di cose a seconda dell'ambiente e del codice in esecuzione.

-Adamo


2

Normalmente significa un accesso non allineato.

Un tentativo di accedere alla memoria che non è fisicamente presente darebbe anche un errore del bus, ma non lo vedrai se stai usando un processore con una MMU e un sistema operativo che non sono buggy, perché non avrai alcun -esistente memoria mappata allo spazio degli indirizzi del processo.


2
Il mio i7 ha sicuramente un MMU, ma ho ancora riscontrato questo errore durante l'apprendimento di C su OS X (passando il puntatore non inizializzato a scanf). Ciò significa che OS X Mavericks è difettoso? Quale sarebbe stato il comportamento su un sistema operativo non difettoso?
Calvin Huang,

2

Stavo ricevendo un errore del bus quando la directory principale era al 100%.


1

La mia ragione per l'errore del bus su Mac OS X era che ho provato ad allocare circa 1 Mb nello stack. Questo ha funzionato bene in un thread, ma quando si utilizza openMP questo porta a errori di bus, poiché Mac OS X ha dimensioni dello stack molto limitate per thread non principali .


1

Sono d'accordo con tutte le risposte sopra. Ecco i miei 2 centesimi per quanto riguarda l'errore BUS:

Non è necessario che si verifichi un errore BUS dalle istruzioni all'interno del codice del programma. Questo può accadere quando si esegue un binario e durante l'esecuzione, il binario viene modificato (sovrascritto da una build o eliminato ecc.).

Verifica se questo è il caso: un modo semplice per verificare se questa è la causa è avviando istanze in esecuzione dello stesso binario ed eseguendo una build. Entrambe le istanze in esecuzione si arresterebbero in modo anomalo con un SIGBUSerrore poco dopo il completamento della compilazione e la sostituzione del file binario (quello in cui entrambe le istanze sono attualmente in esecuzione)

Motivo sottostante: questo perché il sistema operativo scambia le pagine di memoria e in alcuni casi il binario potrebbe non essere completamente caricato in memoria e questi arresti anomali potrebbero verificarsi quando il sistema operativo tenta di recuperare la pagina successiva dallo stesso binario, ma il binario è cambiato dall'ultima volta leggilo.


D'accordo, questa è la causa più comune di errori del bus nella mia esperienza.
itaych

0

Per aggiungere a ciò che ha risposto blxtd sopra, si verificano anche errori del bus quando il processo non può tentare di accedere alla memoria di una particolare 'variabile' .

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

Notate l'uso " involontario " della variabile "i" nel primo "ciclo"? Questo è ciò che sta causando l'errore del bus in questo caso.


Se m> = n, il ciclo esterno verrà eseguito una volta o per niente, a seconda del valore preesistente di i. Se m <n, verrà eseguito indefinitamente con l'indice j in aumento, fino a quando si esauriranno i limiti dell'array e molto probabilmente causeranno un errore di segmentazione, non un errore del bus. Se questo codice viene compilato, non c'è alcun problema ad accedere alla memoria della variabile 'i' stessa. Scusa ma questa risposta è sbagliata.
itaych

0

Ho appena scoperto che su un processore ARMv7 puoi scrivere del codice che ti dà un errore di segmentazione quando non ottimizzato, ma ti dà un errore del bus quando compilato con -O2 (ottimizza di più).

Sto usando il compilatore cross Gnueabihf GCC ARM da Ubuntu 64 bit.


Come risponde alla domanda?
Peter Mortensen,

-1

Un overflow del buffer tipico che provoca un errore del bus è,

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

Qui se la dimensione della stringa tra virgolette ("") è maggiore della dimensione del buf, si ottiene un errore del bus.


1
Eh ... se così fosse, avresti problemi di errore BUS invece degli exploit di distruzione dello stack che leggi tutto il tempo per Windows e altre macchine. Gli errori BUS sono causati dal tentativo di accedere alla "memoria" a cui la macchina semplicemente non può accedere perché l'indirizzo non è valido. (Da qui il termine errore "BUS".) Ciò può essere dovuto a una serie di guasti, inclusi allineamenti non validi, e simili, purché il processore non riesca a posizionare l'indirizzo sulle linee del bus.
Svartalf,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.