O que é um erro de barramento?

254

O que significa a mensagem "erro de barramento" e como ela difere de um segfault?

raldi
fonte
5
Gostaria de adicionar uma explicação simples para ambos: Falha na segmentação significa que você está tentando acessar a memória que não tem permissão (por exemplo, não faz parte do seu programa). No entanto, em um erro de barramento, geralmente significa que você está tentando acessar a memória que não existe (por exemplo, você tenta acessar um endereço em 12G, mas você só tem memória 8G) ou se excede o limite de memória utilizável.
precisa saber é o seguinte
Em qual plataforma você viu isso? PC? Mac? x86? 32/64?
Peter Mortensen

Respostas:

243

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:

  • usando uma instrução de processador com um endereço que não atenda aos requisitos de alinhamento.

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:

  • usando um ponteiro para algo que foi desalocado.
  • usando um ponteiro não inicializado, portanto, falso.
  • usando um ponteiro nulo.
  • transbordando um buffer.

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).

bltxd
fonte
106
Eles não são raros; Eu sou apenas em Exercício 9 de Como aprender C the Hard Way e um já encontrou ...
11684
24
Outra causa de erros de barramento (no Linux, de qualquer maneira) é quando o sistema operacional não pode fazer backup de uma página virtual com memória física (por exemplo, condições de pouca memória ou ficar sem páginas grandes ao usar memória de página enorme). Normalmente, mmap (e malloc) apenas reserve o espaço de endereço virtual, e o kernel atribui a memória física sob demanda (as chamadas falhas de página programável). Faça um malloc grande o suficiente e, em seguida, escreva-o o suficiente para obter um erro de barramento.
Eloff
1
para mim a partição que contém /var/cacheera simplesmente cheia askubuntu.com/a/915520/493379
c33s
2
No meu caso, um método static_casted 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.
Christopher K.
@bltxd Você conhece a natureza dos erros de barramento. ou seja, a mensagem no barramento do anel possui algum mecanismo em que uma parada no anel também aceita uma mensagem que foi enviada por ele, mas para qualquer destino, pois sugere que ele percorreu todo o caminho do anel e não foi aceito. Eu estou supondo que o buffer de preenchimento de linha retorne um status de erro e, quando for retirado, libera o pipeline e chama a microrotina de exceção correta. Isso basicamente exige que o controlador de memória aceitar todos os endereços na sua gama que sugere que quando os bares etc são alterados, ele teria que internamente
Lewis Kelsey
84

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.

Clinton Pierce
fonte
14

mmap exemplo mínimo de POSIX 7

"Erro de barramento" acontece quando o kernel envia SIGBUSpara um processo.

Um exemplo mínimo que o produz porque ftruncatefoi 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 = 0estamos 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.SAe SCTLR_EL1.SA0, resumi os documentos relacionados um pouco mais aqui .

Ciro Santilli adicionou uma nova foto
fonte
11

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

Oli
fonte
1
Depende dos truques desagradáveis ​​que você está fazendo com seu código. Você pode acionar um erro de BUS / Alinhamento de interceptação se fizer algo bobo como fazer matemática de ponteiros e tipecast para acessar o modo problemático (por exemplo, você configura uma matriz uint8_t, adiciona uma, duas ou três ao ponteiro da matriz e depois tipecast para um curto, int ou longo e tente acessar o resultado ofensivo.) Os sistemas X86 praticamente permitem que você faça isso, embora com uma penalidade de desempenho real. ALGUNS sistemas ARMv7 permitirão que você faça isso - mas a maioria dos sistemas ARM, MIPS, Power etc. irão ofender você com isso.
Svartalf
6

Você também pode obter o SIGBUS quando uma página de código não puder ser acessada por algum motivo.

Joshua
fonte
7
Isso muitas vezes acontece quando eu atualizar o arquivo .so durante a execução do processo
poordeveloper
Outra razão para acontecer é se você tentar mmapum arquivo maior do que o tamanho de/dev/shm
ilija139
3

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 strcatanexam 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.

Erik Vesteraas
fonte
Provavelmente, a proteção contra estouro de pilha gera erro de barramento.
Joshua Joshua
1
"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).
precisa saber é o seguinte
3

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 0xdeadf00dem 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).

descontrair
fonte
1
No caso, eu tinha dados [8]; Agora, esse é um múltiplo de 4 em uma arquitetura de 32 bits. Então, está alinhado. Ainda vou receber o erro agora? Além disso, explique, é uma má idéia para uma conversão de tipo de dados para ponteiros. Isso causará erros de desalinhamento em uma arquitetura frágil. Por favor, elabore, isso vai me ajudar.
destro
Heh. Não é tanto a conversão de tipos, mas sim a conversão de tipos em um ponteiro, na qual você fez a matemática dos ponteiros. Observe atentamente o código acima. O compilador cuidadosamente alinhava o ponteiro com o dword para obter dados - e então você estraga tudo no compilador, deslocando a referência por DOIS e fazendo a conversão tipográfica para um acesso muito necessário ao alinhamento pelo dword no que será um limite que não seja o dword.
Svartalf
"Frágil" não é a palavra que eu usaria para tudo isso. As máquinas e o código X86 fazem com que as pessoas façam coisas tolas por um tempo agora, sendo essa uma delas. Repensar seu código se você estiver tendo esse tipo de problema - não é muito bom desempenho no X86, para começar.
Svartalf
@Svartalf: No x86, o acesso a palavras em ponteiros desalinhados é certamente mais lento do que o acesso a palavras em ponteiros alinhados, mas pelo menos historicamente eles foram mais rápidos que o código simples que reúne incondicionalmente coisas de bytes, e certamente são mais simples que o código que tenta para usar uma combinação ideal de operações de tamanhos variados. Eu gostaria que o padrão C incluísse meios de empacotar / descompactar tipos inteiros maiores de / para uma sequência de números inteiros / caracteres menores, de modo a permitir que o compilador use qualquer abordagem que seja melhor em uma determinada plataforma.
Supercat
@Supercat: O problema é que você se safa no X86. Você tenta isso em ARM, MIPS, Power, etc. e terá coisas desagradáveis ​​acontecendo com você. No ARM menor que o Arch V7, seu código terá uma falha de alinhamento - e no V7, você pode, se o tempo de execução estiver definido para ele, lidar com isso com um desempenho SEVERO. Você simplesmente não quer fazer isso. É uma prática ruim, para ser franco. : D
Svartalf 23/04
2

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

Adam Davis
fonte
2

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.

Mark Baker
fonte
2
Meu i7 certamente tem uma MMU, mas ainda assim encontrei esse erro enquanto aprendia C no OS X (passando o ponteiro não inicializado para scanf). Isso significa que o OS X Mavericks está com bugs? Qual teria sido o comportamento em um sistema operacional sem bugs?
Calvin Huang
2

Eu estava recebendo um erro de barramento quando o diretório raiz estava em 100%.

goCards
fonte
1

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 .

Alleo
fonte
1

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 SIGBUSerro 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.

Aditya Vikas Devarapalli
fonte
Concordo, esta é a causa mais comum de erros de barramento na minha experiência.
itaych
0

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.

stuxnetting
fonte
Se m> = n, o loop externo será executado uma vez ou não, dependendo do valor preexistente de i. Se m <n, ele será executado indefinidamente com o índice j aumentando, até você ficar sem limites de sua matriz e provavelmente causar uma falha de segmentação, não um erro de barramento. Se esse código compilar, não haverá problema em acessar a memória da variável 'i'. Desculpe, mas esta resposta está errada.
itaych
0

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.

oromoiluig
fonte
Como isso responde à pergunta?
Peter Mortensen
-1

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.

Vinaya Sagar
fonte
1
Heh ... se esse fosse o caso, você teria preocupações com erros de barramento em vez das explorações de esmagamento de pilha que você lê o tempo todo para Windows e outras máquinas. Os erros de barramento são causados ​​por uma tentativa de acessar a "memória" que a máquina simplesmente não pode acessar porque o endereço é inválido. (Daí o erro "BUS" do termo).) Isso pode ocorrer devido a uma série de falhas, incluindo alinhamentos inválidos e similares, desde que o processador não consiga colocar o endereço nas linhas de barramento.
Svartalf