Co to jest błąd magistrali?

254

Co oznacza komunikat „błąd magistrali” i czym się różni od segfault?

raldi
źródło
5
Chciałbym dodać proste wyjaśnienie zarówno: Błąd segmentacji oznacza, że ​​próbujesz uzyskać dostęp do pamięci, do której nie masz dostępu (np. Nie jest to część twojego programu). Jednak w przypadku błędu magistrali zwykle oznacza to, że próbujesz uzyskać dostęp do pamięci, która nie istnieje (np. Próbujesz uzyskać dostęp do adresu w sieci 12G, ale masz tylko pamięć 8G) lub jeśli przekroczysz limit pamięci użytecznej.
xdevs23
Na jakiej platformie to widziałeś? PC? Prochowiec? x86? 32/64?
Peter Mortensen

Odpowiedzi:

243

Błędy magistrali są obecnie rzadkie na x86 i występują, gdy procesor nie może nawet próbować uzyskać dostępu do pamięci, zwykle:

  • za pomocą instrukcji procesora z adresem, który nie spełnia jego wymagań dotyczących wyrównywania.

Błędy segmentacji występują podczas uzyskiwania dostępu do pamięci, która nie należy do twojego procesu, są bardzo częste i zwykle są wynikiem:

  • za pomocą wskaźnika do czegoś, co zostało zwolnione.
  • za pomocą niezainicjowanego, a więc fałszywego wskaźnika.
  • za pomocą wskaźnika zerowego.
  • przepełnienie bufora.

PS: Mówiąc ściślej, nie jest to manipulowanie samym wskaźnikiem, który spowoduje problemy, lecz dostęp do pamięci, na którą wskazuje (dereferencje).

bltxd
źródło
105
Nie są rzadkie; Jestem właśnie w ćwiczeniu 9 z Jak się nauczyć C na
własnej skórze
24
Inną przyczyną błędów magistrali (zresztą w systemie Linux) jest to, że system operacyjny nie może wykonać kopii zapasowej strony wirtualnej z pamięcią fizyczną (np. Z powodu małej ilości pamięci lub z dużych stron przy użyciu dużej pamięci). Zwykle mmap (i malloc) po prostu zarezerwować wirtualną przestrzeń adresową, a jądro przydziela pamięć fizyczną na żądanie (tzw. błędy strony miękkiej). Wykonaj wystarczająco duży malloc, a następnie napisz do niego wystarczająco dużo, a dostaniesz błąd magistrali.
Eloff
1
dla mnie partycja zawierająca /var/cachebyła po prostu pełna askubuntu.com/a/915520/493379
c33s
2
W moim przypadku metoda static_castzmodyfikowała void *parametr do obiektu, który przechowuje wywołanie zwrotne (jeden atrybut wskazuje na obiekt, a drugi na metodę). Następnie wywoływane jest oddzwonienie. Jednak to, co zostało przekazane, void *było czymś zupełnie innym, a zatem wywołanie metody spowodowało błąd magistrali.
Christopher K.,
@bltxd Czy znasz charakter błędów magistrali. tzn. czy wiadomość na magistrali pierścieniowej ma jakiś mechanizm, w którym zatrzymanie na pierścieniu akceptuje również wiadomość, która została przez niego wysłana, ale do dowolnego miejsca docelowego, ponieważ sugeruje, że okrążyła pierścień i nie została zaakceptowana. Domyślam się, że bufor wypełnienia linii zwraca status błędu, a kiedy się wycofuje, opróżnia potok i wywołuje poprawny wyjątek mikrorurkę. Zasadniczo wymaga to, aby kontroler pamięci zaakceptował cały adres w swoim zakresie, co sugerowałoby, że po zmianie BARów itp. Musiałby to zrobić wewnętrznie
Lewis Kelsey
84

Segfault ma dostęp do pamięci, do której nie masz dostępu. Jest tylko do odczytu, nie masz uprawnień itp.

Błąd magistrali próbuje uzyskać dostęp do pamięci, której prawdopodobnie nie ma. Użyłeś adresu, który nie ma znaczenia dla systemu, lub niewłaściwego rodzaju adresu dla tej operacji.

Clinton Pierce
źródło
14

mmap przykład minimalnej wersji POSIX 7

„Błąd magistrali” występuje, gdy jądro wysyła SIGBUSdo procesu.

Minimalny przykład, który go wytwarza, ponieważ ftruncatezostał zapomniany:

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

Biegnij z:

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

Testowane w Ubuntu 14.04.

POSIX opisuje SIGBUS jako:

Dostęp do niezdefiniowanej części obiektu pamięci.

Specyfikacja mmap mówi, że:

Odniesienia w zakresie adresów rozpoczynającym się od pa i kontynuującym bajty len do całych stron po końcu obiektu powinny skutkować dostarczeniem sygnału SIGBUS.

I shm_open mówi, że generuje obiekty o rozmiarze 0:

Obiekt pamięci współdzielonej ma rozmiar zero.

Tak więc *map = 0dotykamy końca przydzielonego obiektu.

Niezrównany dostęp do pamięci stosu w ARMv8 aarch64

Zostało to wspomniane w: Co to jest błąd magistrali? dla SPARC, ale tutaj podam bardziej powtarzalny przykład.

Wszystko czego potrzebujesz to wolnostojący program 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

Ten program następnie podnosi SIGBUS na Ubuntu 18.04 aarch64, jądro Linuksa 4.15.0 na serwerze ThunderX2 .

Niestety nie mogę go odtworzyć w trybie użytkownika QEMU v4.0.0, nie jestem pewien dlaczego.

Błąd wydaje się być opcjonalny i kontrolowany przez pola SCTLR_ELx.SAi SCTLR_EL1.SA0, streściłem pokrewne dokumenty nieco tutaj .

Ciro Santilli
źródło
11

Wierzę, że jądro podnosi SIGBUS, gdy aplikacja wykazuje przesunięcie danych na szynie danych. Myślę, że skoro większość [?] Nowoczesnych kompilatorów dla większości procesorów pada / wyrównuje dane dla programistów, problemy z wyrównaniem już (przynajmniej) złagodzone, a zatem obecnie nie widać zbyt często SIGBUSA (AFAIK).

Od: tutaj

Oli
źródło
1
Zależy od paskudnych sztuczek, które wykonujesz przy pomocy kodu. Możesz uruchomić błąd magistrali / pułapkę wyrównania, jeśli zrobisz coś głupiego, np. Wykonaj matematykę wskaźnika, a następnie rzut typu, aby uzyskać dostęp do trybu problemu (tj. Skonfigurujesz tablicę uint8_t, dodaj jeden, dwa lub trzy do wskaźnika tablicy, a następnie rzut typu do krótkiego, krótkiego lub dłuższego i spróbuj uzyskać dostęp do obrażającego wyniku.) Systemy X86 pozwolą ci to zrobić, choć z prawdziwą utratą wydajności. NIEKTÓRE systemy ARMv7 pozwoli Ci nie to- ale większość ARM, MIPS, moc, itp będą grouse na ciebie nad nim.
Svartalf,
6

Możesz także uzyskać SIGBUS, gdy z jakiegoś powodu strona kodowa nie może być stronicowana.

Jozuego
źródło
7
Zdarza się to często, gdy aktualizuję plik .so podczas uruchamiania procesu
poordeveloper,
Innym powodem jest próba mmaputworzenia pliku większego niż rozmiar/dev/shm
ilija139,
3

Konkretny przykład błędu magistrali, który właśnie napotkałem podczas programowania C w OS X:

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

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

W przypadku, gdy nie pamiętasz, dokumenty strcatdołączają drugi argument do pierwszego, zmieniając pierwszy argument (odwróć argumenty i działa dobrze). W Linuksie daje to błąd segmentacji (zgodnie z oczekiwaniami), ale w OS X daje błąd magistrali. Czemu? Naprawdę nie wiem.

Erik Vesteraas
źródło
Prawdopodobnie zabezpieczenie przed przepełnieniem stosu podnosi błąd magistrali.
Joshua,
1
"foo"jest przechowywany w segmencie pamięci tylko do odczytu, więc nie można do niego pisać. Nie byłaby to ochrona przed przepełnieniem stosu, tylko ochrona przed zapisem pamięci (jest to dziura w zabezpieczeniach, jeśli Twój program może sam przepisać).
Mark Lakata
3

Klasyczne wystąpienie błędu magistrali występuje w niektórych architekturach, takich jak SPARC (przynajmniej niektóre SPARC, być może to zostało zmienione), kiedy ma się źle ustawiony dostęp. Na przykład:

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

Ten fragment próbuje zapisać 32-bitową wartość całkowitą 0xdeadf00dna adres, który (najprawdopodobniej) nie jest właściwie wyrównany, i wygeneruje błąd magistrali na architekturach, które są „wybredne” pod tym względem. Intel x86, nawiasem mówiąc, nie jest taką architekturą, pozwoliłby na dostęp (choć należy go wykonać wolniej).

rozwijać
źródło
1
W przypadku, gdy miałem dane [8]; Jest to obecnie wielokrotność liczby 4 w architekturze 32-bitowej. To jest wyrównane. Czy nadal będę otrzymywać błąd? Wyjaśnij też, czy niewłaściwy pomysł na konwersję typu danych wskaźników. Czy spowoduje błędy niewłaściwego wyrównywania delikatnej architektury. Proszę opracować, to pomoże mi.
zręczny
Heh To nie jest tak duża konwersja typów, jak konwersja typów na wskaźniku, na którym wykonałeś matematykę wskaźnika. Przyjrzyj się uważnie powyższemu kodowi. Kompilator dokładnie dopasował wskaźnik do danych, a następnie spieprzyłeś wszystko na kompilatorze, przesuwając odwołanie o DWÓCH i rzutując na bardzo potrzebny dostęp do wyrównania dwordów na tym, co będzie granicą niebędącą dwordem.
Svartalf,
„Kruche” nie jest słowem, którego użyłbym do tego wszystkiego. Maszyny i kod X86 sprawiają, że ludzie robią od dłuższego czasu głupie rzeczy, ponieważ jest to jedna z nich. Ponownie przemyśl swój kod, jeśli masz tego rodzaju problem - na początku nie jest zbyt wydajny na X86.
Svartalf,
@Svartalf: Na x86 dostęp do słów na niewyrównanych wskaźnikach jest z pewnością wolniejszy niż dostęp do słów do wyrównanych wskaźników, ale przynajmniej historycznie były one szybsze niż prosty kod, który bezwarunkowo składa rzeczy z bajtów, i z pewnością są prostsze niż kod, który próbuje użyć optymalnej kombinacji operacji o różnych rozmiarach. Chciałbym, aby standard C obejmował środki do pakowania / rozpakowywania większych typów liczb całkowitych do / z sekwencji mniejszych liczb całkowitych / znaków, aby pozwolić kompilatorowi na użycie najlepszego podejścia na danej platformie.
supercat
@Supercat: Chodzi o to, że unikasz tego na X86. Wypróbuj to na ARM, MIPS, Power itp., A przydarzy Ci się nieprzyjemna sytuacja. Na ARM mniejszym niż Arch V7, twój kod ma błąd wyrównywania, a na V7 możesz, JEŻELI twój środowisko wykonawcze jest na to ustawione, poradzić sobie z POWAŻNYM spadkiem wydajności. Po prostu nie chcesz tego robić. Szczerze mówiąc, to złe praktyki. : D
Svartalf,
2

Zależy to od systemu operacyjnego, procesora, kompilatora i ewentualnie innych czynników.

Ogólnie oznacza to, że magistrala procesora nie może wykonać polecenia lub wystąpił konflikt, ale może to oznaczać cały szereg rzeczy w zależności od środowiska i uruchomionego kodu.

-Adam

Adam Davis
źródło
2

Zwykle oznacza to niezrównany dostęp.

Próba dostępu do pamięci, która nie jest fizycznie obecna, również dałaby błąd magistrali, ale nie zobaczysz tego, jeśli używasz procesora z MMU i systemu operacyjnego, który nie jest wadliwy, ponieważ nie będziesz mieć żadnego -istniejąca pamięć odwzorowana na przestrzeń adresową procesu.

Mark Baker
źródło
2
Mój i7 z pewnością ma MMU, ale nadal napotkałem ten błąd podczas nauki C na OS X (przekazując niezainicjowany wskaźnik do scanf). Czy to oznacza, że ​​OS X Mavericks jest wadliwy? Jak wyglądałoby zachowanie systemu operacyjnego bez wad?
Calvin Huang
2

Wystąpił błąd magistrali, gdy katalog główny był w 100%.

goCards
źródło
1

Przyczyną błędu magistrali w systemie Mac OS X było to, że próbowałem przydzielić około 1 Mb na stosie. Działa to dobrze w jednym wątku, ale podczas korzystania z openMP powoduje to błąd magistrali, ponieważ Mac OS X ma bardzo ograniczony rozmiar stosu dla wątków innych niż główne .

Alleo
źródło
1

Zgadzam się ze wszystkimi powyższymi odpowiedziami. Oto moje 2 centy za błąd magistrali:

Błąd magistrali nie musi wynikać z instrukcji zawartych w kodzie programu. Może się to zdarzyć, gdy uruchomisz plik binarny, a podczas wykonywania plik binarny zostanie zmodyfikowany (nadpisany przez kompilację lub usunięty itp.).

Sprawdzanie, czy tak jest: Prostym sposobem sprawdzenia, czy to jest przyczyna, jest uruchomienie uruchomionych instancji tego samego pliku binarnego i uruchomienie kompilacji. Obie działające instancje uległyby awarii z SIGBUSbłędem wkrótce po zakończeniu kompilacji i zastąpiły plik binarny (ten, który obecnie działają obie instancje)

Zasadniczy powód: Dzieje się tak, ponieważ system operacyjny zamienia strony pamięci, aw niektórych przypadkach plik binarny może nie zostać całkowicie załadowany do pamięci, a awarie te występują, gdy system operacyjny próbuje pobrać następną stronę z tego samego pliku binarnego, ale plik binarny zmienił się od czasu ostatniego Przeczytaj to.

Aditya Vikas Devarapalli
źródło
Zgadzam się, jest to najczęstsza przyczyna błędów magistrali w moim doświadczeniu.
itaych
0

Aby dodać do odpowiedzi blxtd powyżej, błędy magistrali występują również wtedy, gdy proces nie może uzyskać dostępu do pamięci określonej „zmiennej” .

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

Czy zauważyłeś „ niezamierzone ” użycie zmiennej „i” w pierwszym „for loop”? To właśnie powoduje błąd magistrali w tym przypadku.

stuxnetting
źródło
Jeśli m> = n, wówczas pętla zewnętrzna zostanie wykonana raz lub wcale, w zależności od wcześniejszej wartości i. Jeśli m <n, to będzie działał w nieskończoność ze wzrostem indeksu j, dopóki nie zabraknie granic tablicy i najprawdopodobniej spowoduje błąd segmentacji, a nie błąd magistrali. Jeśli ten kod się skompiluje, nie będzie problemu z dostępem do pamięci samej zmiennej „i”. Przepraszamy, ale ta odpowiedź jest błędna.
itaych
0

Właśnie dowiedziałem się na własnej skórze, że na procesorze ARMv7 możesz napisać kod, który daje błąd segmentacji, gdy nie jest zoptymalizowany, ale daje błąd magistrali po skompilowaniu z -O2 (zoptymalizuj więcej).

Korzystam z kompilatora krzyżowego GCC ARM gnueabihf z Ubuntu 64-bit.

oromoiluig
źródło
Jak to odpowiada na pytanie?
Peter Mortensen
-1

Typowe przepełnienie bufora, które powoduje błąd magistrali,

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

Jeśli wielkość ciągu w podwójnych cudzysłowach („”) jest większa niż rozmiar buf, oznacza to błąd magistrali.

Vinaya Sagar
źródło
1
Heh ... gdyby tak było, mielibyście obawy związane z błędami magistrali zamiast exploitów niszczących stos, o których cały czas czytacie dla Windows i innych maszyn. Błędy magistrali są spowodowane próbą uzyskania dostępu do „pamięci”, do której urządzenie po prostu nie ma dostępu, ponieważ adres jest nieprawidłowy. (Stąd termin błąd „BUS”). Może to być spowodowane wieloma awariami, w tym nieprawidłowymi ustawieniami i tym podobnymi, o ile procesor nie może umieścić adresu na liniach magistrali.
Svartalf