O que significa a mensagem "erro de barramento" e como ela difere de um segfault?
O que significa a mensagem "erro de barramento" e como ela difere de um segfault?
Respostas:
Atualmente, os erros de barramento são raros no x86 e ocorrem quando o seu processador não pode sequer tentar o acesso à memória solicitado, normalmente:
As falhas de segmentação ocorrem ao acessar a memória que não pertence ao seu processo, elas são muito comuns e geralmente são o resultado de:
PS: Para ser mais preciso, isso não está manipulando o ponteiro propriamente dito que causará problemas, ele está acessando a memória para a qual aponta (desreferenciação).
/var/cache
era simplesmente cheia askubuntu.com/a/915520/493379
static_cast
ed um void *
parâmetro para um objeto que armazena um retorno de chamada (um atributo aponta para o objeto e o outro para o método). Em seguida, o retorno de chamada é chamado. No entanto, o que foi passado como void *
algo completamente diferente e, portanto, a chamada do método causou o erro do barramento.
Um segfault está acessando a memória que você não tem permissão para acessar. É somente leitura, você não tem permissão, etc ...
Um erro de barramento está tentando acessar a memória que não pode estar lá. Você usou um endereço que não faz sentido para o sistema ou o tipo errado de endereço para essa operação.
mmap
exemplo mínimo de POSIX 7
"Erro de barramento" acontece quando o kernel envia SIGBUS
para um processo.
Um exemplo mínimo que o produz porque ftruncate
foi esquecido:
#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;
}
Correr com:
gcc -std=c99 main.c -lrt
./a.out
Testado no Ubuntu 14.04.
O POSIX descreve SIGBUS
como:
Acesso a uma parte indefinida de um objeto de memória.
A especificação mmap diz que:
As referências dentro do intervalo de endereços que começam em pa e continuam com bytes len para páginas inteiras após o final de um objeto devem resultar na entrega de um sinal SIGBUS.
E shm_open
diz que gera objetos de tamanho 0:
O objeto de memória compartilhada tem um tamanho zero.
Então, *map = 0
estamos passando do final do objeto alocado.
Acessos de memória de pilha não alinhados no ARMv8 aarch64
Isso foi mencionado em: O que é um erro de barramento? para SPARC, mas aqui vou fornecer um exemplo mais reproduzível.
Tudo o que você precisa é de um programa independente 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
Esse programa gera o SIGBUS no Ubuntu 18.04 aarch64, Linux kernel 4.15.0 em uma máquina servidor ThunderX2 .
Infelizmente, não consigo reproduzi-lo no modo de usuário QEMU v4.0.0, não sei por que.
A falha parece ser opcional e controlada pelos campos SCTLR_ELx.SA
e SCTLR_EL1.SA0
, resumi os documentos relacionados um pouco mais aqui .
Acredito que o kernel gera o SIGBUS quando um aplicativo exibe desalinhamento de dados no barramento de dados. Eu acho que, já que a maioria dos compiladores modernos para a maioria dos processadores preenche / alinha os dados dos programadores, os problemas de alinhamento de outrora (pelo menos) atenuaram e, portanto, não se vê o SIGBUS com muita frequência atualmente (AFAIK).
De: Aqui
Você também pode obter o SIGBUS quando uma página de código não puder ser acessada por algum motivo.
mmap
um arquivo maior do que o tamanho de/dev/shm
Um exemplo específico de erro de barramento que acabei de encontrar ao programar C no OS X:
#include <string.h>
#include <stdio.h>
int main(void)
{
char buffer[120];
fgets(buffer, sizeof buffer, stdin);
strcat("foo", buffer);
return 0;
}
Caso você não se lembre, os documentos strcat
anexam o segundo argumento ao primeiro, alterando o primeiro argumento (vire os argumentos e funcionará bem). No Linux, isso causa uma falha de segmentação (como esperado), mas no OS X, ocorre um erro no barramento. Por quê? Eu realmente não sei.
"foo"
é armazenado em um segmento de memória somente leitura, portanto, é impossível gravar nele. Não seria uma proteção contra sobrecarga de pilha, apenas proteção contra gravação na memória (isso é uma falha de segurança se o seu programa puder se reescrever).
Uma instância clássica de um erro de barramento é em certas arquiteturas, como o SPARC (pelo menos alguns SPARCs, talvez isso tenha sido alterado), é quando você faz um acesso desalinhado. Por exemplo:
unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;
Esse trecho tenta gravar o valor inteiro de 32 bits 0xdeadf00d
em um endereço que (provavelmente) não está alinhado corretamente e gera um erro de barramento em arquiteturas que são "exigentes" nesse sentido. A Intel x86 é, por sinal, não como uma arquitetura, que permitiria o acesso (embora executá-lo mais lentamente).
Depende do seu sistema operacional, CPU, compilador e possivelmente de outros fatores.
Em geral, isso significa que o barramento da CPU não pôde concluir um comando ou sofreu um conflito, mas isso pode significar uma variedade de coisas, dependendo do ambiente e do código que está sendo executado.
-Adão
Normalmente significa um acesso não alinhado.
Uma tentativa de acessar a memória que não está fisicamente presente também causaria um erro de barramento, mas você não verá isso se estiver usando um processador com uma MMU e um sistema operacional que não esteja com erros, porque você não terá nenhum memória existente mapeada para o espaço de endereço do seu processo.
scanf
). Isso significa que o OS X Mavericks está com bugs? Qual teria sido o comportamento em um sistema operacional sem bugs?
Meu motivo para erro de barramento no Mac OS X foi que eu tentei alocar cerca de 1Mb na pilha. Isso funcionou bem em um thread, mas, ao usar o openMP, isso gera erro de barramento, porque o Mac OS X tem um tamanho de pilha muito limitado para threads não principais .
Eu concordo com todas as respostas acima. Aqui estão meus 2 centavos em relação ao erro BUS:
Um erro de BUS não precisa surgir das instruções contidas no código do programa. Isso pode acontecer quando você está executando um binário e, durante a execução, o binário é modificado (substituído por uma compilação ou excluído etc.).
Verificando se este é o caso:
Uma maneira simples de verificar se essa é a causa é iniciando instâncias em execução do mesmo binário e executando uma construção. Ambas as instâncias em execução travam com um SIGBUS
erro logo após a conclusão da compilação e substituem o binário (aquele que ambas as instâncias estão executando no momento)
Razão subjacente: isso ocorre porque o sistema operacional troca as páginas de memória e, em alguns casos, o binário pode não estar totalmente carregado na memória e essas falhas ocorrem quando o sistema operacional tenta buscar a próxima página do mesmo binário, mas o binário mudou desde a última vez Leia-o.
Para adicionar o que o blxtd respondeu acima, erros de barramento também ocorrem quando seu processo não pode tentar acessar a memória de uma 'variável' específica .
for (j = 0; i < n; j++) {
for (i =0; i < m; i++) {
a[n+1][j] += a[i][j];
}
}
Percebeu o uso ' inadvertido ' da variável 'i' no primeiro 'loop for'? É isso que está causando o erro de barramento neste caso.
Acabei de descobrir da maneira mais difícil que, em um processador ARMv7, você pode escrever um código que gera uma falha de segmentação quando não otimizado, mas gera um erro de barramento quando compilado com -O2 (otimize mais).
Eu estou usando o compilador cruzado GCC ARM gnueabihf do Ubuntu 64 bits.
Um estouro de buffer típico que resulta em erro de barramento é,
{
char buf[255];
sprintf(buf,"%s:%s\n", ifname, message);
}
Aqui, se o tamanho da string entre aspas duplas ("") for maior que o tamanho do buf, ocorrerá um erro de barramento.