Cause une erreur de bus sur x86

introduction

Sur les systèmes x86, nous ne voyons pas beaucoup d'erreurs de bus. J'ai enquêté lorsqu'une erreur de bus (SIGBUS) se produit. J'ai écrit x86 dans le titre, mais j'ai également vérifié SPARC.

À propos de l'erreur de bus

L'erreur de bus (SIGBUS) est, comme son nom l'indique, une erreur qui se produit sur un bus. Se produit lorsqu'une erreur se produit lorsque la CPU fait une demande d'accès à la mémoire physique (elle est censée l'être). Normalement, ce qui est visible pour le programme utilisateur est l'adresse logique, et comme l'adresse physique n'est pas directement touchée, il est difficile pour le programme utilisateur de provoquer une erreur de bus. Il est également connu que cela se produit en raison d'erreurs d'alignement, mais cela ne se produit pas avec x86 car la vérification d'alignement est normalement désactivée. D'après mes recherches, il a été constaté que l'erreur de bus peut être causée de deux manières, "Appeler un appel système qui provoque SIGBUS" et "Faire une erreur d'alignement après avoir activé le contrôle d'alignement".

Utiliser mmap

Un exemple dans StackOverflow. SIGBUS apparaît lorsque vous touchez la zone en dehors de la zone sécurisée par mmap.

bus.c


#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
int main() {
    FILE *f = tmpfile();
    int *m = (int*)mmap(0, 4, PROT_WRITE, MAP_PRIVATE, fileno(f), 0);
    *m = 0;
    return 0;
}
$ gcc bus.c
$ ./a.out
[1]    16871 bus error  ./bus

Il y a une description à cet effet dans man mmap.

The system shall always zero-fill any partial page at the end of an object. Further, the system shall never write out any modified portions of the last page of an object which are beyond its end. References within the address range starting at pa and continuing for len bytes to whole pages following the end of an object shall result in delivery of a SIGBUS signal.

De plus, la [Source] de mMap (http://lxr.free-electrons.com/source/mm/mmap.c#L3063) renvoie explicitement VM_FAULT_SIGBUS.

Exemples d'incohérences d'alignement

Un exemple trouvé dans here qui est sécurisé par char et accessible par int.

bus2.c


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

int main(void)
{
    char *p = (char*)malloc(sizeof(int) + 1);
    memset(p, 0, sizeof(int) + 1);
    p++;
    printf("%d\n", *(int *)p);
    return 0;
}

Cela se termine normalement malgré l'incohérence d'alignement. Cela est dû au fait que l'indicateur de vérification d'alignement (AC) EFLAGS x86 est désactivé. S'il est activé, une erreur de bus se produira.

bus2p.c


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

int main(void)
{
    asm( "pushf\n\torl $0x40000,(%rsp)\n\tpopf");
    char *p = (char*)malloc(sizeof(int) + 1);
    memset(p, 0, sizeof(int) + 1);
    p++;
    printf("%d\n", *(int *)p);
    return 0;
}

Même si l'indicateur AC est activé, l'erreur de bus ne se produit pas sous Mac OS X et se termine normalement.

Accès non autorisé à la zone de chaîne

Essayez d'accéder à la zone de chaîne de caractères.

bus3.c


#include <stdio.h>
#include <stdlib.h>

int
main(void){
  char *s = "test";
  s[0] = '0';
  printf("%s\n",s);
}

Il s'agit d'une erreur de segmentation (SIGSEGV) sur Linux, mais donne une erreur de bus sur Mac.


$ clang bus3.c
$ ./a.out
Bus error: 10

Résumé

En résumé, ça ressemble à ça. J'ai également inclus les résultats de Cygwin. J'avais aussi un système SPARC Linux, donc je l'ai recherché (sauf pour bus2p.c, qui utilise des assemblages en ligne).

bus.c bus2.c bus2p.c bus3.c
Linux SIGBUS Réussite SIGBUS SIGSEGV
Mac OS X SIGBUS Réussite Réussite SIGBUS
Cygwin SIGBUS Réussite Réussite SIGSEGV
SPARC SIGSEGV SIGBUS ---- SIGSEGV

Dans le système x86 actuel, l'erreur de bus n'apparaît pas dans le programme utilisateur à moins que vous n'activiez explicitement la vérification d'alignement pour accéder à l'alignement incohérent ou que vous utilisiez un appel système qui émet SIGBUS. L'exception est Mac, qui a été signalé comme la distinction entre SIGBUS et SIGSEGV est ambiguë, et une erreur de bus se produit simplement en accédant à une chaîne de caractères. Même avec le même Linux, le comportement est assez différent avec SPARC. Je pense que c'est vrai que SIGBUS est émis en raison d'un alignement incorrect, mais je ne sais pas pourquoi il est émis SIGSEGV en raison d'un mMap incorrect (n'est-ce pas décidé par POSIX?).

Je n'aime pas le fait que tous les systèmes que j'ai vérifiés soient différents.

Recommended Posts

Cause une erreur de bus sur x86
Créer un environnement de développement Python sur Mac OS X
Créer un environnement de développement Python avec OS X Lion
Windows10 (x64) Créer nativement un environnement d'apprentissage automatique
Un commentaire sur l'algorithme de Boruta
Mettez Python 3.x dans Ubuntu
Exécutez Tensorflow 2.x sur Python 3.7
Créer une salle de classe sur Jupyterhub
Mémo sur Mac OS X