Qu'est-ce qu'une erreur de bus?


255

Que signifie le message "erreur de bus" et en quoi diffère-t-il d'un défaut de segmentation?


5
Je voudrais ajouter une explication simple pour les deux: l'erreur de segmentation signifie que vous essayez d'accéder à la mémoire à laquelle vous n'êtes pas autorisé (par exemple, elle ne fait pas partie de votre programme). Cependant, sur une erreur de bus, cela signifie généralement que vous essayez d'accéder à une mémoire qui n'existe pas (par exemple, vous essayez d'accéder à une adresse à 12G mais vous n'avez que de la mémoire 8G) ou si vous dépassez la limite de mémoire utilisable.
xdevs23

Sur quelle plateforme avez-vous vu cela? PC? Mac? x86? 32/64?
Peter Mortensen

Réponses:


244

Les erreurs de bus sont rares de nos jours sur x86 et se produisent lorsque votre processeur ne peut même pas tenter l'accès à la mémoire demandé, généralement:

  • en utilisant une instruction de processeur avec une adresse qui ne satisfait pas ses exigences d'alignement.

Des erreurs de segmentation se produisent lors de l'accès à une mémoire qui n'appartient pas à votre processus, elles sont très courantes et sont généralement le résultat de:

  • en utilisant un pointeur sur quelque chose qui a été désalloué.
  • en utilisant un pointeur non initialisé donc faux.
  • en utilisant un pointeur nul.
  • débordement d'un tampon.

PS: Pour être plus précis, ce n'est pas manipuler le pointeur lui-même qui causera des problèmes, c'est accéder à la mémoire vers laquelle il pointe (déréférencement).


106
Ils ne sont pas rares; Je suis juste à l'exercice 9 de How to Learn C the Hard Way et j'en ai déjà rencontré un ...
11684

24
Une autre cause d'erreurs de bus (sous Linux de toute façon) est lorsque le système d'exploitation ne peut pas sauvegarder une page virtuelle avec de la mémoire physique (par exemple, des conditions de mémoire insuffisante ou des pages volumineuses lors de l'utilisation d'une mémoire de page énorme). Généralement mmap (et malloc) juste réservez l'espace d'adressage virtuel, et le noyau assigne la mémoire physique à la demande (ce que l'on appelle les erreurs de page logicielle). Créez un malloc assez grand, puis écrivez-y suffisamment et vous obtiendrez une erreur de bus.
Eloff

1
pour moi, la partition contenant /var/cacheétait simplement pleine askubuntu.com/a/915520/493379
c33s

2
Dans mon cas, une méthode a static_castédité un void *paramètre sur un objet qui stocke un rappel (un attribut pointe vers l'objet et l'autre vers la méthode). Ensuite, le rappel est appelé. Cependant, ce qui a été transmis void *était quelque chose de complètement différent et donc l'appel de méthode a provoqué l'erreur de bus.
Christopher K.

@bltxd Connaissez-vous la nature des erreurs de bus. c'est-à-dire que le message sur le bus de l'anneau a un mécanisme où un arrêt sur l'anneau accepte également un message qui a été envoyé par lui mais vers n'importe quelle destination car il suggère qu'il a fait le tour de l'anneau et n'a pas été accepté. Je suppose que le tampon de remplissage de ligne renvoie un état d'erreur et lorsqu'il se retire, il vide le pipeline et appelle la microroutine d'exception correcte. Cela nécessite essentiellement que le contrôleur de mémoire accepte toutes les adresses de sa plage, ce qui suggère que lorsque les BARS, etc. sont modifiés, il devrait le faire en interne
Lewis Kelsey

84

Un segfault accède à la mémoire à laquelle vous n'êtes pas autorisé à accéder. C'est en lecture seule, vous n'avez pas la permission, etc ...

Une erreur de bus tente d'accéder à une mémoire qui ne peut pas exister. Vous avez utilisé une adresse qui n'a pas de sens pour le système ou le mauvais type d'adresse pour cette opération.


14

mmap exemple minimal POSIX 7

Une "erreur de bus" se produit lorsque le noyau envoie SIGBUSà un processus.

Un exemple minimal qui le produit car a ftruncateété oublié:

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

Courir avec:

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

Testé dans Ubuntu 14.04.

POSIX décrit SIGBUS comme:

Accès à une partie non définie d'un objet mémoire.

La spécification mmap dit que:

Les références dans la plage d'adresses commençant à pa et se poursuivant pendant len ​​octets à des pages entières après la fin d'un objet entraîneront la livraison d'un signal SIGBUS.

Et shm_open dit qu'il génère des objets de taille 0:

L'objet de mémoire partagée a une taille de zéro.

Donc, *map = 0nous touchons au-delà de la fin de l'objet alloué.

Accès à la mémoire de pile non alignés dans ARMv8 aarch64

Cela a été mentionné à: Qu'est-ce qu'une erreur de bus? pour SPARC, mais ici je vais fournir un exemple plus reproductible.

Tout ce dont vous avez besoin est un programme autonome 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

Ce programme lève ensuite SIGBUS sur Ubuntu 18.04 aarch64, noyau Linux 4.15.0 sur une machine serveur ThunderX2 .

Malheureusement, je ne peux pas le reproduire en mode utilisateur QEMU v4.0.0, je ne sais pas pourquoi.

La faute semble être facultative et contrôlée par les champs SCTLR_ELx.SAet SCTLR_EL1.SA0, j'ai résumé les documents connexes un peu plus loin ici .


11

Je crois que le noyau soulève SIGBUS lorsqu'une application présente un désalignement des données sur le bus de données. Je pense que puisque la plupart des compilateurs modernes pour la plupart des processeurs remplissent / alignent les données pour les programmeurs, les problèmes d'alignement d'antan (au moins) atténués, et donc on ne voit pas SIGBUS trop souvent de nos jours (AFAIK).

De: ici


1
Cela dépend des mauvais tours que vous faites avec votre code. Vous pouvez déclencher une erreur BUS / Alignment Trap si vous faites quelque chose de stupide comme faire des calculs de pointeur et ensuite transtyper pour accéder à un mode problème (c'est-à-dire que vous configurez un tableau uint8_t, ajoutez un, deux ou trois au pointeur du tableau, puis transtypez à un court, un entier ou un long et essayez d'accéder au résultat incriminé.) Les systèmes X86 vous permettront à peu près de le faire, mais avec une réelle pénalité de performance. CERTAINS systèmes ARMv7 vous le permettront, mais la plupart des ARM, MIPS, Power, etc.
Svartalf

6

Vous pouvez également obtenir SIGBUS lorsqu'une page de codes ne peut pas être paginée pour une raison quelconque.


7
Cela se produit souvent lorsque je mets à jour le fichier .so lors de l'exécution du processus
poordeveloper

Une autre raison peut se produire si vous essayez mmapun fichier plus grand que la taille de/dev/shm
ilija139

4

Une instance classique d'une erreur de bus se trouve sur certaines architectures, telles que le SPARC (au moins certains SPARC, peut-être que cela a été changé), c'est lorsque vous effectuez un accès mal aligné. Par exemple:

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

Cet extrait de code essaie d'écrire la valeur entière 32 bits 0xdeadf00ddans une adresse qui (très probablement) n'est pas correctement alignée, et générera une erreur de bus sur les architectures qui sont "pointilleuses" à cet égard. Le Intel x86 n'est d'ailleurs pas une telle architecture, il autoriserait l'accès (quoiqu'il s'exécute plus lentement).


1
Au cas où, j'avais des données [8]; Il s'agit désormais d'un multiple de 4 dans une architecture 32 bits. Donc, il est aligné. Vais-je toujours obtenir l'erreur maintenant? Veuillez également expliquer s'il s'agit d'une mauvaise idée de conversion de type de données pour les pointeurs. Cela entraînera-t-il des erreurs d'alignement sur une architecture fragile. Veuillez élaborer, cela m'aidera.
dextère

Il h. Ce n'est pas tant la conversion de type que la conversion de type sur un pointeur sur lequel vous avez effectué des calculs de pointeur. Regardez attentivement le code ci-dessus. Le compilateur a soigneusement aligné votre pointeur pour les données sur dword, puis vous avez tout vissé sur le compilateur en compensant la référence par DEUX et en effectuant une conversion de type pour un accès à un alignement dword sur ce qui va être une limite non dword.
Svartalf

«Fragile» n'est pas le mot que j'utiliserais pour tout cela. Les machines et le code X86 ont fait que les gens font des choses plutôt idiotes depuis un moment maintenant, ceci étant l'un d'entre eux. Repensez votre code si vous rencontrez ce genre de problème - ce n'est pas très performant sur X86 pour commencer.
Svartalf

@Svartalf: Sur x86, les accès aux mots sur les pointeurs non alignés sont certainement plus lents que les accès aux mots aux pointeurs alignés, mais au moins historiquement, ils ont été plus rapides que le code simple qui assemble inconditionnellement les choses à partir d'octets, et ils sont certainement plus simples que le code qui essaie d'utiliser une combinaison optimale d'opérations de tailles variées. Je souhaite que la norme C inclue des moyens de compresser / décompresser des types d'entiers plus grands vers / à partir d'une séquence de plus petits nombres entiers / caractères afin de permettre au compilateur d'utiliser la meilleure approche sur une plate-forme donnée.
supercat

@Supercat: Le truc, c'est que vous vous en sortez avec X86. Vous essayez ceci sur ARM, MIPS, Power, etc. et vous allez avoir des choses désagréables qui vous arrivent. Sur ARM inférieur à Arch V7, votre code aura un échec d'alignement et sur V7, vous pouvez, SI votre temps d'exécution est défini pour lui, le gérer avec un hit de performance SÉVÈRE. Vous ne voulez tout simplement pas FAIRE cela. Ce sont de mauvaises pratiques, pour être franc. : D
Svartalf

3

Un exemple spécifique d'une erreur de bus que je viens de rencontrer lors de la programmation de C sur OS X:

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

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

Dans le cas où vous ne vous souvenez pas, la documentation strcatajoute le deuxième argument au premier en changeant le premier argument (retournez les arguments et cela fonctionne bien). Sous Linux, cela donne un défaut de segmentation (comme prévu), mais sous OS X, cela donne une erreur de bus. Pourquoi? Je ne sais vraiment pas.


La protection contre le débordement de pile soulève probablement une erreur de bus.
Joshua

1
"foo"est stocké dans un segment de mémoire en lecture seule, il est donc impossible d'y écrire. Ce ne serait pas une protection contre le débordement de pile, juste une protection en écriture mémoire (c'est un trou de sécurité si votre programme peut se réécrire).
Mark Lakata

2

Cela dépend de votre système d'exploitation, de votre processeur, de votre compilateur et éventuellement d'autres facteurs.

En général, cela signifie que le bus CPU n'a pas pu exécuter une commande ou a subi un conflit, mais cela peut signifier toute une gamme de choses en fonction de l'environnement et du code en cours d'exécution.

-Adam


2

Cela signifie normalement un accès non aligné.

Une tentative d'accès à une mémoire qui n'est pas physiquement présente donnerait également une erreur de bus, mais vous ne le verrez pas si vous utilisez un processeur avec une MMU et un OS qui n'est pas bogué, car vous n'aurez pas de non -mémoire existante mappée à l'espace d'adressage de votre processus.


2
Mon i7 a certainement une MMU, mais j'ai quand même rencontré cette erreur lors de l'apprentissage de C sur OS X (en passant le pointeur non initialisé scanf). Est-ce à dire que OS X Mavericks est buggé? Quel aurait été le comportement sur un système d'exploitation non bogué?
Calvin Huang

2

J'obtenais une erreur de bus lorsque le répertoire racine était à 100%.


1

Ma raison de l'erreur de bus sur Mac OS X était que j'essayais d'allouer environ 1 Mo sur la pile. Cela fonctionnait bien dans un thread, mais lors de l'utilisation d'openMP, cela entraîne une erreur de bus, car Mac OS X a une taille de pile très limitée pour les threads non principaux .


1

Je suis d'accord avec toutes les réponses ci-dessus. Voici mes 2 cents concernant l'erreur BUS:

Une erreur de BUS n'a pas besoin de provenir des instructions dans le code du programme. Cela peut se produire lorsque vous exécutez un binaire et pendant l'exécution, le binaire est modifié (écrasé par une génération ou supprimé, etc.).

Vérifier si c'est le cas: Un moyen simple de vérifier si c'est la cause est de lancer des instances en cours d'exécution du même binaire et d'exécuter une build. Les deux instances en cours d'exécution se bloqueraient avec une SIGBUSerreur peu de temps après la fin de la génération et remplaceraient le binaire (celui que les deux instances exécutent actuellement)

Raison sous-jacente: cela est dû au fait que le système d'exploitation échange les pages de la mémoire et, dans certains cas, le binaire peut ne pas être entièrement chargé en mémoire et ces plantages se produisent lorsque le système d'exploitation tente de récupérer la page suivante du même binaire, mais le binaire a changé depuis sa dernière lis le.


D'accord, c'est la cause la plus fréquente d'erreurs de bus dans mon expérience.
itaych

0

Pour ajouter à ce que blxtd a répondu ci-dessus, des erreurs de bus se produisent également lorsque votre processus ne peut pas tenter d'accéder à la mémoire d'une «variable» particulière .

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

Vous remarquez l'utilisation « par inadvertance » de la variable «i» dans la première «boucle for»? C'est ce qui cause l'erreur de bus dans ce cas.


Si m> = n, la boucle externe s'exécutera une fois ou pas du tout, selon la valeur préexistante de i. Si m <n, alors il s'exécutera indéfiniment avec l'index j croissant, jusqu'à ce que vous manquiez de limites de votre tableau et provoquiez très probablement une erreur de segmentation, pas une erreur de bus. Si ce code se compile, il n'y a aucun problème pour accéder à la mémoire de la variable «i» elle-même. Désolé mais cette réponse est fausse.
itaych

0

Je viens de découvrir à la dure que sur un processeur ARMv7, vous pouvez écrire du code qui vous donne un défaut de segmentation lorsqu'il n'est pas optimisé, mais cela vous donne une erreur de bus lorsqu'il est compilé avec -O2 (optimisez davantage).

J'utilise le compilateur croisé GCC ARM gnueabihf d'Ubuntu 64 bits.


Comment cela répond-il à la question?
Peter Mortensen

-1

Un débordement de tampon typique qui entraîne une erreur de bus est,

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

Ici, si la taille de la chaîne entre guillemets ("") est supérieure à la taille buf, cela donne une erreur de bus.


1
Hé ... si tel était le cas, vous auriez des problèmes d'erreur BUS au lieu des exploits de destruction de pile que vous lisez tout le temps pour Windows et d'autres machines. Les erreurs de BUS sont causées par une tentative d'accès à la "mémoire" à laquelle la machine ne peut tout simplement pas accéder car l'adresse n'est pas valide. (D'où le terme erreur "BUS".) Cela peut être dû à une multitude de défaillances, y compris des alignements invalides, et ainsi de suite tant que le processeur ne peut pas placer l'adresse sur les lignes de bus.
Svartalf
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.