버스 오류는 무엇입니까?


254

"버스 오류"메시지는 무엇을 의미하며 segfault와 어떻게 다릅니 까?


5
세 가지 모두에 대한 간단한 설명을 추가하고 싶습니다. 세그먼테이션 오류는 허용되지 않은 메모리에 액세스하려고 함을 의미합니다 (예 : 프로그램의 일부가 아님). 그러나 버스 오류가 발생하면 일반적으로 존재하지 않는 메모리 (예 : 12G에서 주소에 액세스하려고하지만 8G 메모리 만 있음)에 액세스하려고하거나 사용 가능한 메모리 한계를 초과하는 경우를 의미합니다.
xdevs23

어떤 플랫폼에서 이걸 보았습니까? PC? 맥? x86? 32/64?
피터 Mortensen

답변:


243

버스 오류는 오늘날 x86에서 드물며 프로세서가 요청한 메모리 액세스를 시도 할 수 없을 때 발생합니다.

  • 정렬 요구 사항을 충족하지 않는 주소로 프로세서 명령어 사용

세그먼트 결함은 프로세스에 속하지 않은 메모리에 액세스 할 때 발생하며 매우 일반적이며 다음과 같은 결과입니다.

  • 할당 해제 된 무언가에 대한 포인터 사용.
  • 초기화되지 않은 가짜 포인터를 사용합니다.
  • 널 포인터 사용.
  • 버퍼 넘침.

추신 : 더 정확하게 말하면 문제를 일으킬 포인터 자체를 조작하는 것이 아니라 가리키는 메모리에 액세스하는 것입니다 (역 참조).


105
그들은 드문 일이 아닙니다. 나는 C를 어려운 방법으로 배우는 방법의 연습 9에 있고 이미 하나를 만났습니다 ...
11684

24
버스 오류 (Linux 어쨌든)의 또 다른 원인은 운영 체제가 실제 메모리가있는 가상 페이지를 백업 할 수없는 경우입니다 (예 : 메모리 부족 상태 또는 대용량 페이지 메모리를 사용할 때 대용량 페이지가 없음). 일반적으로 mmap (및 malloc) 가상 주소 공간을 예약하면 커널은 요청시 실제 메모리 (소프트 페이지 오류라고 함)를 할당합니다. 충분히 큰 malloc을 작성하고 충분히 작성하면 버스 오류가 발생합니다.
Eloff

1
나를 위해 포함하는 파티션은 /var/cache단순히 가득했습니다 askubuntu.com/a/915520/493379
c33s

2
제 경우, 방법은 static_cast에드 void *객체에 해당 점포 콜백 (객체 속성 한 점에있어서 다른) 파라미터. 그런 다음 콜백이 호출됩니다. 그러나 전달 된 내용은 void *완전히 다른 것으로서 메소드 호출로 인해 버스 오류가 발생했습니다.
Christopher K.

@bltxd 버스 오류의 본질을 알고 있습니까? 즉, 링 버스의 메시지에는 링의 정류장이 링이 보낸 메시지를 수신하지만 링의 끝까지 갔다가 수락되지 않은 메시지를 수신하는 메커니즘이 있습니다. 줄 채우기 버퍼가 오류 상태를 반환하고 은퇴 할 때 파이프 라인을 플러시하고 올바른 예외 마이크로 루틴을 호출한다고 생각합니다. 이것은 기본적으로 메모리 컨트롤러가 범위 내의 모든 주소를 받아 들여야하므로 BAR 등이 변경되면 내부적으로 변경해야 함을 나타냅니다
Lewis Kelsey

84

segfault가 액세스가 허용되지 않은 메모리에 액세스하고 있습니다. 읽기 전용이며 권한이 없습니다.

버스 오류가있을 수없는 메모리에 액세스하려고합니다. 시스템에 무의미한 주소를 사용했거나 해당 작업에 잘못된 종류의 주소를 사용했습니다.


14

mmap 최소 POSIX 7 예

커널 SIGBUS이 프로세스로 전송할 때 "버스 오류"가 발생 합니다.

ftruncate잊혀져 서 생성하는 최소한의 예 :

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

로 실행 :

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

우분투 14.04에서 테스트되었습니다.

POSIX 는 다음 SIGBUS같이 설명 합니다.

메모리 객체의 정의되지 않은 부분에 액세스

의 mmap 사양은 말한다 :

pa에서 시작하여 객체의 끝 이후에 전체 페이지에 대해 len 바이트 동안 계속되는 주소 범위 내의 참조는 SIGBUS 신호를 전달해야합니다.

그리고 크기가 0 인 객체를 생성 shm_open 한다고 말합니다 .

공유 메모리 객체의 크기는 0입니다.

따라서 *map = 0할당 된 객체의 끝을지나갑니다.

ARMv8 aarch64에서 정렬되지 않은 스택 메모리 액세스

: 이것은에서 언급 한 버스 오류가 무엇입니까? SPARC의 경우보다 재현 가능한 예를 제공합니다.

필요한 것은 독립형 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

그런 다음이 프로그램은 Ubuntu 18.04 aarch64, ThunderX2 서버 시스템의 Linux 커널 4.15.0에서 SIGBUS를 발생 시킵니다 .

불행히도, QEMU v4.0.0 사용자 모드에서는 재현 할 수 없습니다. 이유가 확실하지 않습니다.

장애는에 의해 선택 및 제어 것으로 보인다 SCTLR_ELx.SASCTLR_EL1.SA0, 내가 관련 문서를 요약 한 분야 추가 여기에 약간 .


11

응용 프로그램이 데이터 버스에서 데이터 정렬이 잘못되면 커널이 SIGBUS를 발생시킵니다. 대부분의 프로세서를위한 대부분의 현대 컴파일러가 프로그래머를위한 데이터를 패딩 / 정렬하기 때문에 요어의 정렬 문제가 (적어도) 완화되어 요즘 SIGBUS가 너무 자주 보이지 않는다고 생각합니다 (AFAIK).

부터 : 여기


1
코드로 수행하는 불쾌한 트릭에 따라 다릅니다. 포인터 수학처럼 바보 같은 일을하고 문제 모드에 액세스하기 위해 타입 캐스트하면 BUS 오류 / 정렬 트랩을 트리거 할 수 있습니다 (예 : uint8_t 배열을 설정하고 배열의 포인터에 1, 2 또는 3을 추가 한 다음 타입 캐스트) X86 시스템은 실제 성능 저하에도 불구하고 거의 그렇게 할 수 있습니다. 일부 ARMv7 시스템에서는이 작업을 수행 할 수 있지만 대부분의 ARM, MIPS, Power 등은 그에 대한 문제를 야기합니다.
Svartalf

6

어떤 이유로 코드 페이지를 페이징 할 수없는 경우 SIGBUS를 얻을 수도 있습니다.


7
나는이 과정을 실행하는 동안 .so를 파일을 업데이트 할 때 자주 발생합니다
poordeveloper

또 다른 이유는 다음 mmap보다 큰 파일 을 시도하면/dev/shm
ilija139

3

OS X에서 C를 프로그래밍하는 동안 방금 발생한 버스 오류의 특정 예 :

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

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

문서가 기억 나지 않는 경우 strcat첫 번째 인수를 변경하여 두 번째 인수를 첫 번째 인수에 추가합니다 (인수를 넘기면 제대로 작동합니다). 리눅스에서는 예상대로 세그먼테이션 오류가 발생하지만 OS X에서는 버스 오류가 발생합니다. 왜? 나는 정말로 모른다.


스택 오버플로 방지로 인해 버스 오류가 발생했을 수 있습니다.
Joshua

1
"foo"읽기 전용 메모리 세그먼트에 저장되므로 쓰기가 불가능합니다. 스택 오버플로 방지가 아니라 메모리 쓰기 보호 (프로그램 자체를 다시 작성할 수있는 경우 보안 허점)입니다.
Mark Lakata 2016 년

3

버스 오류의 한 가지 전형적인 인스턴스는 SPARC (적어도 일부 SPARC , 아마도 변경되었을 수 있음) 와 같은 특정 아키텍처에서 잘못 정렬 된 액세스를 수행하는 경우입니다. 예를 들어 :

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

이 코드 조각은 32 비트 정수 값 0xdeadf00d을 적절하게 정렬되지 않은 주소 에 쓰려고 시도 하며 이와 관련하여 "피킹"된 아키텍처에서 버스 오류를 생성합니다. 인텔은 x86 그런데,입니다 하지 이러한 아키텍처는 (더 천천히 실행이기는하지만)에 대한 액세스를 허용합니다.


1
데이터가있는 경우 [8]; 이것은 이제 32 비트 아키텍처에서 4의 배수입니다. 따라서 정렬됩니다. 여전히 오류가 발생합니까? 또한 포인터의 데이터 유형 변환에 나쁜 생각인지 설명하십시오. 깨지기 쉬운 아키텍처에서 정렬 오류가 발생합니까? 좀 더 정교하게 도와주세요.
dexterous

허. 포인터 계산을 수행 한 포인터에서 유형 변환을 수행하는 것처럼 유형 변환이 그리 많지 않습니다. 위 코드를 주의 깊게 보십시오 . 컴파일러는 데이터에 대한 포인터를 신중하게 dword 정렬 한 다음 두 개의 참조를 오프셋하고 typecasting을 사용하여 비 워드 경계가 될 대상에 대한 dword 정렬 액세스가 필요하도록 컴파일러의 모든 것을 망칩니다.
Svartalf

"Fragile"은이 모든 것에 사용하는 단어가 아닙니다. X86 기계와 코드는 사람들이 한동안 꽤 어리석은 일을하게 만들었습니다. 이런 종류의 문제가 발생하면 코드를 다시 생각하십시오. X86에서 처음에는 성능이 좋지 않습니다.
Svartalf

@Svartalf : x86에서 정렬되지 않은 포인터의 단어 액세스는 정렬 된 포인터의 단어 액세스보다 확실히 느리지 만 적어도 역사적으로 바이트를 사용하여 무조건 바이트를 조립하는 간단한 코드보다 빠르며 시도하는 코드보다 확실히 간단합니다 다양한 크기의 작업을 최적으로 조합하여 사용합니다. C 표준에 더 큰 정수 유형을 더 작은 정수 / 문자 시퀀스로 패킹 / 언 패킹하는 방법이 컴파일러에 주어진 플랫폼에서 가장 적합한 방법을 사용할 수 있기를 바랍니다.
supercat

@Supercat : 문제는 X86에서 해결하는 것입니다. ARM, MIPS, Power 등에서이 작업을 시도하면 불쾌한 일이 발생합니다. Arch V7보다 적은 ARM에서는 코드에 정렬 실패가 발생하며 V7에서는 런타임이 설정된 경우 심각한 성능 저하로 처리 할 수 ​​있습니다. 당신은 단순히 이것을하고 싶지 않습니다. 무례한 것은 나쁜 관행입니다. : D
Svartalf

2

OS, CPU, 컴파일러 및 기타 요인에 따라 다릅니다.

일반적으로 CPU 버스가 명령을 완료 할 수 없거나 충돌이 발생했지만 환경 및 코드 실행에 따라 모든 범위를 의미 할 수 있습니다.

-아담


2

일반적으로 정렬되지 않은 액세스를 의미합니다.

물리적으로 존재하지 않는 메모리에 액세스하려고하면 버스 오류가 발생하지만 MMU가없는 프로세서와 버그가없는 OS를 사용하는 경우이 오류가 표시되지 않습니다. 프로세스의 주소 공간에 매핑 된 기존 메모리


2
내 i7에는 확실히 MMU가 있지만 OS X에서 C를 배우는 동안 여전히이 오류가 발생했습니다 (초기화되지 않은 포인터 전달 scanf). 그것은 OS X Mavericks가 버그가 있다는 것을 의미합니까? 버그가없는 OS의 동작은 무엇입니까?
Calvin Huang

2

루트 디렉토리가 100 % 일 때 버스 오류가 발생했습니다.


1

Mac OS X에서 버스 오류가 발생한 이유는 스택에 약 1MB를 할당하려고했기 때문입니다. 이것은 하나의 스레드에서 잘 작동했지만, Mac OS X에는 비 주요 스레드의 스택 크기 가 매우 제한되어 있기 때문에 openMP를 사용할 때 버스 오류가 발생 합니다 .


1

위의 모든 답변에 동의합니다. BUS 오류와 관련된 2 센트는 다음과 같습니다.

프로그램 코드 내의 명령어에서 BUS 오류가 발생하지 않아도됩니다. 바이너리를 실행할 때와 실행 중에 바이너리가 수정됩니다 (빌드로 덮어 쓰기 또는 삭제 등).

이것이 맞는지 확인 : 이것이 원인인지 확인하는 간단한 방법은 동일한 바이너리의 실행중인 인스턴스를 시작하고 빌드를 실행하는 것입니다. SIGBUS빌드가 완료되고 바이너리 (두 인스턴스가 현재 실행중인 인스턴스)를 교체 한 직후에 실행중인 두 인스턴스가 모두 오류와 함께 충돌 합니다.

기본 이유 : OS가 메모리 페이지를 스왑하고 일부 경우 바이너리가 메모리에 완전히로드되지 않을 수 있으며 OS가 동일한 바이너리에서 다음 페이지를 가져 오려고 할 때 이러한 충돌이 발생하지만 바이너리는 마지막 이후 변경되었습니다. 읽어.


동의, 이것은 내 경험에서 버스 오류의 가장 일반적인 원인입니다.
itaych

0

blxtd가 위에서 응답 한 것을 더하기 위해, 프로세스 가 특정 'variable'의 메모리에 액세스하려고 시도 할 수없는 경우에도 버스 오류가 발생합니다 .

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

첫 번째 'for 루프' 에서 변수 'i' 의 ' 부주의 한 '사용법을 주목하십시오 . 이것이이 경우 버스 오류의 원인입니다.


m> = n이면 외부 루프는 기존 값 i에 따라 한 번만 실행되거나 전혀 실행되지 않습니다. m <n이면 j 인덱스가 증가함에 따라 무한정으로 실행되며 배열의 경계가 부족하여 버스 오류가 아닌 세그먼트 화 오류가 발생할 가능성이 높습니다. 이 코드가 컴파일되면 변수 'i'자체의 메모리에 액세스하는 데 아무런 문제가 없습니다. 죄송하지만이 답변은 잘못되었습니다.
itaych

0

방금 ARMv7 프로세서에서 최적화되지 않은 경우 세그먼테이션 오류를 발생시키는 코드를 작성할 수 있지만 -O2 (더 최적화)로 컴파일하면 버스 오류가 발생하는 어려운 방법을 발견했습니다.

우분투 64 비트에서 GCC ARM gnueabihf 크로스 컴파일러를 사용하고 있습니다.


이것이 어떻게 질문에 대답합니까?
Peter Mortensen

-1

버스 오류가 발생하는 일반적인 버퍼 오버 플로우는 다음과 같습니다.

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

큰 따옴표 ( "")로 묶인 문자열의 크기가 buf 크기보다 크면 버스 오류가 발생합니다.


1
허 ..이 경우라면 Windows 및 기타 시스템의 모든 시간에 대해 읽는 스택 스매싱 익스플로잇 대신 버스 오류 문제가 발생합니다. BUS 오류는 주소가 유효하지 않기 때문에 기계가 단순히 액세스 할 수없는 "메모리"에 액세스하려는 시도로 인해 발생합니다. (따라서 "BUS"오류라는 용어입니다.) 프로세서가 버스 라인에 주소를 배치 할 수없는 한 유효하지 않은 정렬 등을 포함하여 수많은 장애로 인해 발생할 수 있습니다.
Svartalf
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.