Was ist ein Busfehler?


254

Was bedeutet die Meldung "Busfehler" und wie unterscheidet sie sich von einem Segfault?


5
Ich möchte eine einfache Erklärung für beide hinzufügen: Segmentierungsfehler bedeutet, dass Sie versuchen, auf Speicher zuzugreifen, auf den Sie nicht zugreifen dürfen (z. B. ist er nicht Teil Ihres Programms). Bei einem Busfehler bedeutet dies normalerweise, dass Sie versuchen, auf nicht vorhandenen Speicher zuzugreifen (z. B. wenn Sie versuchen, auf eine Adresse mit 12 G zuzugreifen, aber nur 8 G Speicher haben) oder wenn Sie die Grenze des nutzbaren Speichers überschreiten.
xdevs23

Auf welcher Plattform haben Sie das gesehen? PC? Mac? x86? 32/64?
Peter Mortensen

Antworten:


243

Busfehler sind heutzutage auf x86 selten und treten auf, wenn Ihr Prozessor nicht einmal den angeforderten Speicherzugriff versuchen kann, normalerweise:

  • Verwenden eines Prozessorbefehls mit einer Adresse, die seine Ausrichtungsanforderungen nicht erfüllt.

Segmentierungsfehler treten beim Zugriff auf Speicher auf, der nicht zu Ihrem Prozess gehört. Sie sind sehr häufig und resultieren normalerweise aus:

  • Verwenden eines Zeigers auf etwas, das freigegeben wurde.
  • unter Verwendung eines nicht initialisierten, daher gefälschten Zeigers.
  • mit einem Nullzeiger.
  • Puffer überlaufen.

PS: Genauer gesagt manipuliert dies nicht den Zeiger selbst, der Probleme verursacht, sondern greift auf den Speicher zu, auf den es zeigt (Dereferenzierung).


105
Sie sind nicht selten; Ich bin gerade bei Übung 9 von Wie man C auf die harte Tour lernt und bin bereits auf eine gestoßen ...
11684

24
Eine weitere Ursache für Busfehler (ohnehin unter Linux) ist, dass das Betriebssystem eine virtuelle Seite nicht mit physischem Speicher sichern kann (z. B. Bedingungen mit wenig Speicher oder nicht genügend große Seiten, wenn großer Seitenspeicher verwendet wird) Reservieren Sie den virtuellen Adressraum, und der Kernel weist den physischen Speicher bei Bedarf zu (sogenannte Soft-Page-Fehler). Erstellen Sie ein ausreichend großes Malloc, und schreiben Sie dann auf genügend davon, und Sie erhalten einen Busfehler.
Eloff

1
für mich war die Partition, die enthielt, /var/cacheeinfach voll askubuntu.com/a/915520/493379
c33s

2
In meinem Fall hat eine Methode static_casteinen void *Parameter für ein Objekt festgelegt, in dem ein Rückruf gespeichert ist (ein Attribut zeigt auf das Objekt und das andere auf die Methode). Dann wird der Rückruf aufgerufen. Was jedoch als void *etwas völlig anderes übergeben wurde, verursachte der Methodenaufruf den Busfehler.
Christopher K.

@bltxd Kennen Sie die Art der Busfehler? dh hat die Nachricht auf dem Ringbus einen Mechanismus, bei dem ein Stopp auf dem Ring auch eine Nachricht akzeptiert, die von ihm gesendet wurde, jedoch an ein beliebiges Ziel, da dies darauf hindeutet, dass sie den gesamten Ring umrundet und nicht akzeptiert wurde. Ich vermute, dass der Zeilenfüllpuffer einen Fehlerstatus zurückgibt. Wenn er in den Ruhestand geht, wird die Pipeline geleert und die richtige Ausnahmemikroroutine aufgerufen. Dies erfordert grundsätzlich, dass der Speichercontroller alle Adressen in seinem Bereich akzeptiert, was darauf hindeutet, dass bei einer Änderung der BARs usw. intern
Lewis Kelsey

84

Ein Segfault greift auf Speicher zu, auf den Sie nicht zugreifen dürfen. Es ist schreibgeschützt, Sie haben keine Erlaubnis usw.

Ein Busfehler versucht, auf Speicher zuzugreifen, der möglicherweise nicht vorhanden sein kann. Sie haben eine Adresse verwendet, die für das System bedeutungslos ist, oder die falsche Art von Adresse für diesen Vorgang.


14

mmap minimales POSIX 7 Beispiel

"Busfehler" tritt auf, wenn der Kernel SIGBUSan einen Prozess sendet .

Ein minimales Beispiel, das es produziert, weil ftruncatees vergessen wurde:

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

Laufen Sie mit:

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

Getestet in Ubuntu 14.04.

POSIX beschreibt SIGBUS als:

Zugriff auf einen undefinierten Teil eines Speicherobjekts.

Die mmap-Spezifikation besagt Folgendes :

Verweise innerhalb des Adressbereichs, die bei pa beginnen und für len Bytes bis zu ganzen Seiten nach dem Ende eines Objekts fortgesetzt werden, führen zur Lieferung eines SIGBUS-Signals.

Und shm_open sagt, dass es Objekte der Größe 0 generiert:

Das gemeinsam genutzte Speicherobjekt hat eine Größe von Null.

Also bei *map = 0uns über das Ende des zugeordneten Objekt berühren.

Nicht ausgerichtete Stapelspeicherzugriffe in ARMv8 aarch64

Dies wurde erwähnt unter: Was ist ein Busfehler? für SPARC, aber hier werde ich ein reproduzierbareres Beispiel geben.

Sie benötigen lediglich ein freistehendes aarch64-Programm:

.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

Dieses Programm löst dann SIGBUS unter Ubuntu 18.04 aarch64, Linux-Kernel 4.15.0 auf einem ThunderX2-Server aus .

Leider kann ich es im QEMU v4.0.0-Benutzermodus nicht reproduzieren. Ich bin mir nicht sicher, warum.

Der Fehler scheint durch die optionale und gesteuert zu werden SCTLR_ELx.SAund SCTLR_EL1.SA0Felder, ich habe die damit verbundenen Dokumente zusammengefasst ein bisschen weiter hier .


11

Ich glaube, der Kernel löst SIGBUS aus, wenn eine Anwendung eine Datenfehlausrichtung auf dem Datenbus aufweist. Ich denke, da die meisten [?] Modernen Compiler für die meisten Prozessoren die Daten für die Programmierer auffüllen / ausrichten, werden die Ausrichtungsprobleme von früher (zumindest) gemildert, und daher sieht man SIGBUS heutzutage nicht allzu oft (AFAIK).

Von: Hier


1
Kommt auf die fiesen Tricks an, die du mit deinem Code machst. Sie können einen BUS-Fehler / eine Ausrichtungsfalle auslösen, wenn Sie etwas Dummes tun, z. B. Zeigerberechnung durchführen und dann für den Zugriff auf einen Problemmodus typisieren (dh Sie richten ein Array uint8_t ein, fügen dem Zeiger des Arrays eins, zwei oder drei hinzu und tippen dann zu kurz, int oder lang und versuchen, auf das fehlerhafte Ergebnis zuzugreifen.) Mit X86-Systemen können Sie dies so ziemlich tun, wenn auch mit einem echten Leistungsverlust. Einige ARMv7-Systeme lassen Sie dies tun - aber die meisten ARM, MIPS, Power usw. werden Sie darüber meckern.
Svartalf

6

Sie können SIGBUS auch erhalten, wenn eine Codepage aus irgendeinem Grund nicht ausgelagert werden kann.


7
Dies passiert oft, wenn ich die .so-Datei aktualisiere, während der Prozess ausgeführt wird
Poordeveloper

Ein weiterer Grund ist, wenn Sie versuchen, mmapeine Datei größer als die Größe von/dev/shm
ilija139

3

Ein spezielles Beispiel für einen Busfehler, den ich gerade beim Programmieren von C unter OS X festgestellt habe:

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

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

Falls Sie sich nicht erinnern, strcathängen die Dokumente das zweite Argument an das erste an, indem Sie das erste Argument ändern (drehen Sie die Argumente um und es funktioniert einwandfrei). Unter Linux gibt dies (wie erwartet) einen Segmentierungsfehler, unter OS X jedoch einen Busfehler. Warum? Ich weiß es wirklich nicht.


Wahrscheinlich löst der Stapelüberlaufschutz einen Busfehler aus.
Joshua

1
"foo"wird in einem schreibgeschützten Speichersegment gespeichert, so dass es unmöglich ist, darauf zu schreiben. Es wäre kein Stapelüberlaufschutz, sondern nur ein Speicherschreibschutz (dies ist eine Sicherheitslücke, wenn Ihr Programm sich selbst neu schreiben kann).
Mark Lakata

3

Eine klassische Instanz eines Busfehlers tritt bei bestimmten Architekturen auf, z. B. beim SPARC (zumindest bei einigen SPARCs, möglicherweise wurde dies geändert), wenn Sie einen falsch ausgerichteten Zugriff ausführen. Zum Beispiel:

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

Dieses Snippet versucht, den 32-Bit-Integer-Wert 0xdeadf00din eine Adresse zu schreiben, die (höchstwahrscheinlich) nicht richtig ausgerichtet ist, und erzeugt einen Busfehler bei Architekturen, die in dieser Hinsicht "wählerisch" sind. Der Intel x86 ist übrigens keine solche Architektur, er würde den Zugriff ermöglichen (wenn auch langsamer ausführen).


1
Für den Fall, ich hatte Daten [8]; Dies ist jetzt ein Vielfaches von 4 in einer 32-Bit-Architektur. Es ist also ausgerichtet. Bekomme ich den Fehler jetzt noch? Bitte erläutern Sie auch, ob eine Datentypkonvertierung für Zeiger eine schlechte Idee ist. Verursacht es Fehlausrichtungsfehler in einer fragilen Architektur? Bitte erläutern Sie, es wird mir helfen.
Geschickte

Heh. Es ist nicht so sehr die Typkonvertierung als vielmehr die Typkonvertierung für einen Zeiger, für den Sie Zeigerberechnung durchgeführt haben. Schauen Sie sich den obigen Code genau an. Der Compiler hat Ihren Zeiger sorgfältig auf Daten ausgerichtet - und dann vermasseln Sie alles am Compiler, indem Sie die Referenz um ZWEI versetzen und auf einen sehr dword-ausgerichteten Zugriff auf eine Nicht-Dword-Grenze typisieren.
Svartalf

"Fragile" ist nicht das Wort, das ich für all das verwenden würde. X86-Maschinen und -Code haben Leute schon seit einiger Zeit dazu gebracht, ziemlich dumme Dinge zu tun, dies ist einer von ihnen. Überdenken Sie Ihren Code, wenn Sie solche Probleme haben - unter X86 ist er zunächst nicht sehr leistungsfähig.
Svartalf

@Svartalf: Unter x86 sind Wortzugriffe auf nicht ausgerichtete Zeiger sicherlich langsamer als Wortzugriffe auf ausgerichtete Zeiger, aber zumindest in der Vergangenheit waren sie schneller als einfacher Code, der Dinge bedingungslos aus Bytes zusammensetzt, und sie sind sicherlich einfacher als Code, der es versucht eine optimale Kombination von Operationen unterschiedlicher Größe zu verwenden. Ich wünschte, der C-Standard würde Mittel zum Packen / Entpacken größerer Ganzzahltypen in / aus einer Folge kleinerer Ganzzahlen / Zeichen enthalten, damit der Compiler den auf einer bestimmten Plattform am besten geeigneten Ansatz verwenden kann.
Supercat

@ Supercat: Die Sache ist das - du kommst damit auf X86 davon. Sie versuchen dies mit ARM, MIPS, Power usw. und es werden Ihnen böse Dinge passieren. Bei ARM unter Arch V7 tritt bei Ihrem Code ein Ausrichtungsfehler auf. Bei V7 können Sie, sofern Ihre Laufzeit dafür festgelegt ist, mit einem SEVERE-Leistungstreffer umgehen. Sie wollen das einfach nicht tun. Es ist eine schlechte Praxis, stumpf zu sein. : D
Svartalf

2

Dies hängt von Ihrem Betriebssystem, Ihrer CPU, Ihrem Compiler und möglicherweise anderen Faktoren ab.

Im Allgemeinen bedeutet dies, dass der CPU-Bus einen Befehl nicht ausführen oder einen Konflikt erleiden konnte. Dies kann jedoch je nach Umgebung und ausgeführtem Code eine ganze Reihe von Dingen bedeuten.

-Adam


2

Dies bedeutet normalerweise einen nicht ausgerichteten Zugriff.

Ein Versuch, auf Speicher zuzugreifen, der physisch nicht vorhanden ist, würde ebenfalls zu einem Busfehler führen. Dies wird jedoch nicht angezeigt, wenn Sie einen Prozessor mit einer MMU und einem Betriebssystem verwenden, das nicht fehlerhaft ist, da Sie keinen Fehler haben - Vorhandener Speicher, der dem Adressraum Ihres Prozesses zugeordnet ist.


2
Mein i7 hat sicherlich eine MMU, aber ich bin immer noch auf diesen Fehler gestoßen, als ich C unter OS X gelernt habe (indem ich den nicht initialisierten Zeiger an übergeben habe scanf). Bedeutet das, dass OS X Mavericks fehlerhaft ist? Wie wäre das Verhalten auf einem nicht fehlerhaften Betriebssystem gewesen?
Calvin Huang

2

Ich habe einen Busfehler erhalten, als das Stammverzeichnis 100% war.


1

Mein Grund für einen Busfehler unter Mac OS X war, dass ich versucht habe, ungefähr 1 MB auf dem Stapel zuzuweisen. Dies funktionierte in einem Thread gut, aber bei Verwendung von openMP führt dies zu Busfehlern, da Mac OS X eine sehr begrenzte Stapelgröße für Nicht-Haupt-Threads hat .


1

Ich stimme allen obigen Antworten zu. Hier sind meine 2 Cent bezüglich des BUS-Fehlers:

Ein BUS-Fehler muss nicht aus den Anweisungen im Programmcode hervorgehen. Dies kann passieren, wenn Sie eine Binärdatei ausführen und während der Ausführung die Binärdatei geändert wird (durch einen Build überschrieben oder gelöscht usw.).

Überprüfen, ob dies der Fall ist: Eine einfache Möglichkeit, zu überprüfen, ob dies die Ursache ist, besteht darin, laufende Instanzen derselben Binärdatei zu starten und einen Build auszuführen. Beide laufenden Instanzen würden SIGBUSkurz nach Abschluss des Builds mit einem Fehler abstürzen und die Binärdatei ersetzen (diejenige, die beide Instanzen derzeit ausführen).

Grund: Dies liegt daran, dass das Betriebssystem Speicherseiten austauscht und in einigen Fällen die Binärdatei möglicherweise nicht vollständig im Speicher geladen ist. Diese Abstürze treten auf, wenn das Betriebssystem versucht, die nächste Seite aus derselben Binärdatei abzurufen, die Binärdatei sich jedoch seit ihrer letzten Änderung geändert hat Lies es.


Einverstanden ist dies meiner Erfahrung nach die häufigste Ursache für Busfehler.
17.

0

Um die oben beantwortete Antwort von blxtd zu ergänzen, treten auch Busfehler auf, wenn Ihr Prozess nicht versuchen kann, auf den Speicher einer bestimmten 'Variablen' zuzugreifen .

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

Beachten Sie die " versehentliche " Verwendung der Variablen "i" in der ersten "for-Schleife"? Das ist es, was in diesem Fall den Busfehler verursacht.


Wenn m> = n ist, wird die äußere Schleife je nach dem bereits vorhandenen Wert von i einmal oder gar nicht ausgeführt. Wenn m <n ist, wird es mit zunehmendem j-Index unbegrenzt ausgeführt, bis Sie die Grenzen Ihres Arrays überschreiten und höchstwahrscheinlich einen Segmentierungsfehler und keinen Busfehler verursachen. Wenn dieser Code kompiliert wird, ist es kein Problem, auf den Speicher der Variablen 'i' selbst zuzugreifen. Entschuldigung, aber diese Antwort ist falsch.
17.

0

Ich habe gerade herausgefunden, wie schwierig es ist, auf einem ARMv7-Prozessor Code zu schreiben, der bei Nichtoptimierung einen Segmentierungsfehler verursacht, beim Kompilieren mit -O2 jedoch einen Busfehler (mehr optimieren).

Ich benutze den GCC ARM gnueabihf Cross Compiler von Ubuntu 64 Bit.


Wie beantwortet dies die Frage?
Peter Mortensen

-1

Ein typischer Pufferüberlauf, der zu einem Busfehler führt, ist:

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

Wenn hier die Größe der Zeichenfolge in doppelten Anführungszeichen ("") größer als die Puffergröße ist, wird ein Busfehler ausgegeben.


1
Heh ... wenn dies der Fall wäre, hätten Sie BUS-Fehler anstelle der Stack-Smashing-Exploits, über die Sie ständig für Windows und andere Computer gelesen haben. BUS-Fehler werden durch den Versuch verursacht, auf "Speicher" zuzugreifen, auf den das Gerät einfach nicht zugreifen kann, weil die Adresse ungültig ist. (Daher der Begriff "BUS" -Fehler.) Dies kann auf eine Vielzahl von Fehlern zurückzuführen sein, einschließlich ungültiger Ausrichtungen und dergleichen, solange der Prozessor die Adresse nicht auf den Busleitungen platzieren kann.
Svartalf
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.