Lỗi xe buýt là gì?


254

Thông báo "lỗi bus" có nghĩa là gì và nó khác với segfault như thế nào?


5
Tôi muốn thêm một lời giải thích đơn giản cho cả hai: Lỗi phân đoạn có nghĩa là bạn đang cố truy cập vào bộ nhớ mà bạn không được phép (ví dụ: đây không phải là một phần của chương trình của bạn). Tuy nhiên, đối với lỗi bus, điều đó thường có nghĩa là bạn đang cố truy cập bộ nhớ không tồn tại (ví dụ: bạn cố truy cập địa chỉ ở 12G nhưng bạn chỉ có bộ nhớ 8G) hoặc nếu bạn vượt quá giới hạn của bộ nhớ có thể sử dụng.
xdevs23

Trên nền tảng nào bạn đã thấy điều này? MÁY TÍNH? Mac? x86? 32/64?
Peter Mortensen

Câu trả lời:


243

Lỗi bus hiện nay rất hiếm trên x86 và xảy ra khi bộ xử lý của bạn thậm chí không thể thử truy cập bộ nhớ được yêu cầu, thường là:

  • sử dụng một lệnh xử lý với một địa chỉ không thỏa mãn các yêu cầu căn chỉnh của nó.

Lỗi phân đoạn xảy ra khi truy cập bộ nhớ không thuộc về quy trình của bạn, chúng rất phổ biến và thường là kết quả của:

  • sử dụng một con trỏ đến một cái gì đó đã được giải quyết.
  • sử dụng một con trỏ chưa được khởi tạo từ đó không có thật.
  • sử dụng một con trỏ null
  • tràn một bộ đệm.

PS: Nói chính xác hơn, đây không phải là thao tác với chính con trỏ sẽ gây ra sự cố, nó truy cập vào bộ nhớ mà nó trỏ tới (hội nghị truyền hình).


105
Chúng không hiếm; Tôi chỉ ở Bài tập 9 từ Cách học C theo cách khó khăn và đã gặp phải một ...
11684 26/03/13

24
Một nguyên nhân khác gây ra lỗi bus (dù sao trên Linux) là khi hệ điều hành không thể sao lưu trang ảo bằng bộ nhớ vật lý (ví dụ: điều kiện bộ nhớ thấp hoặc hết các trang lớn khi sử dụng bộ nhớ trang lớn.) Thông thường là mmap (và malloc) dành không gian địa chỉ ảo và kernel gán bộ nhớ vật lý theo yêu cầu (được gọi là lỗi trang mềm.) Tạo một malloc đủ lớn, sau đó viết cho đủ số đó và bạn sẽ gặp lỗi xe buýt.
Eloff

1
đối với tôi, phân vùng chứa /var/cachechỉ đơn giản là đầy đủ Askubfox.com/a/915520/493379
c33s

2
Trong trường hợp của tôi, một phương thức static_casted một void *tham số cho một đối tượng lưu trữ một cuộc gọi lại (một thuộc tính trỏ đến đối tượng và một thuộc tính khác cho phương thức). Sau đó, cuộc gọi lại được gọi. Tuy nhiên, những gì đã được thông qua void *là một cái gì đó hoàn toàn khác và do đó, cuộc gọi phương thức gây ra lỗi bus.
Christopher K.

@bltxd Bạn có biết bản chất của lỗi xe buýt. tức là tin nhắn trên xe buýt vòng có một số cơ chế trong đó điểm dừng trên vòng cũng chấp nhận một tin nhắn được gửi bởi nó nhưng đến bất kỳ đích nào vì nó gợi ý rằng nó đã đi hết vòng và không được chấp nhận. Tôi đoán bộ đệm điền dòng trả về trạng thái lỗi và khi nó rút ra, nó sẽ xóa đường ống và gọi microroutine ngoại lệ chính xác. Điều này về cơ bản đòi hỏi bộ điều khiển bộ nhớ chấp nhận tất cả địa chỉ trong phạm vi của nó, điều này sẽ gợi ý rằng khi các BAR vv được thay đổi, nó sẽ phải ở bên trong
Lewis Kelsey

84

Một segfault đang truy cập bộ nhớ mà bạn không được phép truy cập. Nó chỉ đọc, bạn không có quyền, v.v ...

Một lỗi xe buýt đang cố truy cập vào bộ nhớ không thể có ở đó. Bạn đã sử dụng một địa chỉ vô nghĩa đối với hệ thống hoặc loại địa chỉ sai cho hoạt động đó.


14

mmap ví dụ POSIX tối thiểu 7

"Lỗi bus" xảy ra khi kernel gửi SIGBUStới một tiến trình.

Một ví dụ tối thiểu tạo ra nó vì ftruncateđã bị lãng quên:

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

Chạy với:

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

Đã thử nghiệm trong Ubuntu 14.04.

POSIX mô tả SIGBUS như sau:

Truy cập vào một phần không xác định của một đối tượng bộ nhớ.

Thông số mmap nói rằng:

Các tham chiếu trong phạm vi địa chỉ bắt đầu từ pa và tiếp tục cho các byte len đến toàn bộ các trang sau khi kết thúc một đối tượng sẽ dẫn đến việc phát tín hiệu SIGBUS.

shm_open nói rằng nó tạo ra các đối tượng có kích thước 0:

Đối tượng bộ nhớ chia sẻ có kích thước bằng không.

Vì vậy, tại *map = 0chúng tôi đang chạm qua cuối của đối tượng được phân bổ.

Truy cập bộ nhớ ngăn xếp không được chỉ định trong ARMv8 aarch64

Điều này đã được đề cập tại: Lỗi xe buýt là gì? cho SPARC, nhưng ở đây tôi sẽ cung cấp một ví dụ dễ tái tạo hơn.

Tất cả bạn cần là một chương trình aarch64 độc lập:

.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

Chương trình đó sau đó tăng SIGBUS trên Ubuntu 18.04 aarch64, nhân Linux 4.15.0 trong máy chủ ThunderX2 .

Thật không may, tôi không thể sao chép nó trên chế độ người dùng QEMU v4.0.0, tôi không chắc tại sao.

Lỗi dường như là tùy chọn và được kiểm soát bởi SCTLR_ELx.SASCTLR_EL1.SA0các trường, tôi đã tóm tắt các tài liệu liên quan xa hơn một chút ở đây .


11

Tôi tin rằng hạt nhân tăng SIGBUS khi một ứng dụng thể hiện sự sai lệch dữ liệu trên bus dữ liệu. Tôi nghĩ rằng vì hầu hết [?] Trình biên dịch hiện đại cho hầu hết các bộ xử lý đệm / căn chỉnh dữ liệu cho các lập trình viên, nên các vấn đề liên kết của giảm bớt (ít nhất) đã giảm bớt và do đó người ta không thấy SIGBUS quá thường xuyên trong những ngày này (AFAIK).

Từ: Đây


1
Phụ thuộc vào các thủ thuật khó chịu mà bạn đang làm với mã của mình. Bạn có thể kích hoạt Bẫy lỗi / Bẫy căn chỉnh nếu bạn làm điều gì đó ngớ ngẩn như làm toán con trỏ và sau đó đánh máy để truy cập vào chế độ vấn đề (ví dụ: Bạn thiết lập một mảng uint8_t, thêm một, hai hoặc ba vào con trỏ của mảng và sau đó gõ đến một đoạn ngắn, int hoặc dài và cố gắng truy cập vào kết quả vi phạm.) Các hệ thống X86 sẽ cho phép bạn làm điều này, mặc dù ở một hình phạt hiệu suất thực sự. MỘT SỐ hệ thống ARMv7 sẽ cho phép bạn làm điều này - nhưng hầu hết ARM, MIPS, Power, v.v. sẽ tập hợp bạn qua nó.
Svartalf

6

Bạn cũng có thể nhận SIGBUS khi một trang mã không thể được phân trang vì một số lý do.


7
Điều này thường xảy ra khi tôi cập nhật tệp .so trong khi chạy quy trình
poordeveloper

Một lý do khác sẽ xảy ra là nếu bạn cố gắng gửi mmapmột tệp lớn hơn kích thước của/dev/shm
ilija139

3

Một ví dụ cụ thể về lỗi bus tôi vừa gặp khi lập trình C trên OS X:

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

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

Trong trường hợp bạn không nhớ các tài liệu sẽ strcatnối đối số thứ hai với đối số thứ nhất bằng cách thay đổi đối số thứ nhất (lật các đối số và nó hoạt động tốt). Trên linux, lỗi này phân đoạn (như mong đợi), nhưng trên OS X, nó báo lỗi bus. Tại sao? Tôi thực sự không biết.


Có lẽ ngăn xếp tràn bảo vệ làm tăng lỗi xe buýt.
Joshua

1
"foo"được lưu trữ trong một phân đoạn chỉ đọc của bộ nhớ, vì vậy không thể ghi vào nó. Nó sẽ không được bảo vệ chống chồng, chỉ là bảo vệ ghi bộ nhớ (đây là lỗ hổng bảo mật nếu chương trình của bạn có thể tự viết lại).
Đánh dấu Lakata

3

Một trường hợp kinh điển của lỗi xe buýt là trên một số kiến ​​trúc nhất định, chẳng hạn như SPARC (ít nhất là một số SPARC, có thể điều này đã bị thay đổi), là khi bạn thực hiện truy cập sai. Ví dụ:

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

Đoạn mã này cố gắng ghi giá trị số nguyên 32 bit 0xdeadf00dvào một địa chỉ (rất có thể) không được căn chỉnh chính xác và sẽ tạo ra lỗi bus trên các kiến ​​trúc "kén chọn" trong vấn đề này. Nhân tiện, Intel x86 không phải là một kiến ​​trúc như vậy, nó sẽ cho phép truy cập (mặc dù thực thi nó chậm hơn).


1
Trong trường hợp, tôi đã có dữ liệu [8]; Đây là bội số của 4 trong kiến ​​trúc 32 bit. Vì vậy, nó được liên kết. Tôi vẫn sẽ nhận được lỗi bây giờ? Ngoài ra, vui lòng giải thích, đó có phải là một ý tưởng tồi đối với chuyển đổi loại dữ liệu cho con trỏ. Nó sẽ gây ra lỗi căn chỉnh sai trên một kiến ​​trúc dễ vỡ. Xin hãy giải thích, Nó sẽ giúp tôi.
khéo léo

Heh. Đó không phải là quá nhiều chuyển đổi kiểu như bạn đang thực hiện chuyển đổi loại trên một con trỏ mà bạn đã thực hiện toán học con trỏ. Nhìn kỹ vào đoạn mã trên. Trình biên dịch đã cẩn thận dword căn chỉnh con trỏ của bạn cho dữ liệu - và sau đó bạn làm hỏng mọi thứ trên trình biên dịch bằng cách bù đắp tham chiếu bằng TWO và typecasting thành một rất nhiều nhu cầu được truy cập từ phù hợp với những gì sẽ là một ranh giới không phải là từ.
Svartalf

"Mong manh" không phải là từ tôi sử dụng cho tất cả những điều này. Máy và mã X86 đã khiến mọi người làm những việc khá ngớ ngẩn trong một thời gian, đây là một trong số đó. Xem xét lại mã của bạn nếu bạn gặp phải loại vấn đề này - nó không thực sự hiệu quả trên X86.
Svartalf

@Svartalf: Trên x86, truy cập từ trên các con trỏ không được phân bổ chắc chắn chậm hơn truy cập từ vào các con trỏ được căn chỉnh, nhưng ít nhất về mặt lịch sử, chúng đã nhanh hơn mã đơn giản, lắp ráp mọi thứ một cách vô điều kiện và chúng chắc chắn đơn giản hơn mã cố để sử dụng kết hợp tối ưu các hoạt động kích thước khác nhau. Tôi muốn tiêu chuẩn C sẽ bao gồm các phương tiện đóng gói / giải nén các loại số nguyên lớn hơn đến / từ một chuỗi các số nguyên / ký tự nhỏ hơn để cho phép trình biên dịch sử dụng bất kỳ cách tiếp cận nào là tốt nhất trên nền tảng nhất định.
supercat

@Supercat: Vấn đề là thế này - bạn thoát khỏi nó trên X86. Bạn thử điều này trên ARM, MIPS, Power, v.v. và bạn sẽ nhận được những điều khó chịu xảy ra với mình. Trên ARM ít hơn Arch V7, mã của bạn sẽ bị lỗi căn chỉnh - và trên V7, bạn có thể, NẾU thời gian chạy của bạn được đặt cho nó, xử lý nó với một lần nhấn hiệu năng SEVERE. Bạn chỉ đơn giản là không muốn làm điều này. Đó là thực hành xấu, để được thẳng thừng. : D
Svartalf

2

Nó phụ thuộc vào hệ điều hành, CPU, Trình biên dịch của bạn và có thể các yếu tố khác.

Nói chung, điều đó có nghĩa là bus CPU không thể hoàn thành một lệnh hoặc bị xung đột, nhưng điều đó có thể có nghĩa là toàn bộ mọi thứ tùy thuộc vào môi trường và mã được chạy.

-Adam


2

Nó thường có nghĩa là một truy cập không liên kết.

Nỗ lực truy cập bộ nhớ không có mặt thực tế cũng sẽ gây ra lỗi xe buýt, nhưng bạn sẽ không thấy điều này nếu bạn đang sử dụng bộ xử lý có MMU và HĐH không có lỗi, vì bạn sẽ không gặp phải lỗi nào bộ nhớ hiện tại được ánh xạ tới không gian địa chỉ của quá trình của bạn.


2
I7 của tôi chắc chắn có MMU, nhưng tôi vẫn gặp lỗi này khi học C trên OS X (chuyển con trỏ chưa được khởi tạo sang scanf). Điều đó có nghĩa là OS X Mavericks có lỗi? Điều gì sẽ là hành vi trên một hệ điều hành không có lỗi?
Calvin Huang

2

Tôi đã nhận được một lỗi xe buýt khi thư mục gốc là 100%.



1

Tôi đồng ý với tất cả các câu trả lời ở trên. Dưới đây là 2 xu của tôi liên quan đến lỗi BUS:

Một lỗi Bus không cần phải phát sinh từ các hướng dẫn trong mã của chương trình. Điều này có thể xảy ra khi bạn đang chạy nhị phân và trong quá trình thực thi, nhị phân được sửa đổi (được ghi đè bởi bản dựng hoặc bị xóa, v.v.).

Xác minh nếu đây là trường hợp: Một cách đơn giản để kiểm tra xem đây có phải là nguyên nhân hay không bằng cách khởi chạy các phiên bản đang chạy của cùng một nhị phân và để chạy một bản dựng. Cả hai phiên bản đang chạy sẽ SIGBUSgặp lỗi ngay sau khi quá trình xây dựng kết thúc và thay thế nhị phân (phiên bản mà cả hai phiên bản hiện đang chạy)

Lý do ngầm: Điều này là do HĐH hoán đổi các trang bộ nhớ và trong một số trường hợp, nhị phân có thể không được tải hoàn toàn vào bộ nhớ và các sự cố này sẽ xảy ra khi HĐH cố gắng tìm nạp trang tiếp theo từ cùng một nhị phân, nhưng nhị phân đã thay đổi kể từ lần cuối đọc nó.


Đồng ý, đây là nguyên nhân phổ biến nhất gây ra lỗi xe buýt theo kinh nghiệm của tôi.
itaych

0

Để thêm vào những gì blxtd đã trả lời ở trên, lỗi bus cũng xảy ra khi quá trình của bạn không thể truy cập vào bộ nhớ của một 'biến' cụ thể .

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

Lưu ý cách sử dụng ' vô tình ' của biến 'i' trong 'vòng lặp' đầu tiên? Đó là những gì gây ra lỗi xe buýt trong trường hợp này.


Nếu m> = n thì vòng lặp bên ngoài sẽ thực hiện một lần hoặc không, tùy thuộc vào giá trị có sẵn của i. Nếu m <n thì nó sẽ chạy vô thời hạn với chỉ số j tăng, cho đến khi bạn hết giới hạn của mảng và rất có thể gây ra lỗi phân đoạn, không phải lỗi bus. Nếu mã này biên dịch thì sẽ không có vấn đề gì khi truy cập vào bộ nhớ của chính biến 'i'. Xin lỗi nhưng câu trả lời này là sai.
itaych

0

Tôi chỉ phát hiện ra một cách khó khăn là trên bộ xử lý ARMv7, bạn có thể viết một số mã gây ra lỗi phân đoạn khi không được tối ưu hóa, nhưng nó gây ra lỗi bus khi được biên dịch với -O2 (tối ưu hóa nhiều hơn).

Tôi đang sử dụng trình biên dịch chéo gnuispihf GCC ARM từ Ubuntu 64 bit.


Làm thế nào để trả lời câu hỏi này?
Peter Mortensen

-1

Một lỗi tràn bộ đệm điển hình dẫn đến lỗi Bus là,

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

Ở đây nếu kích thước của chuỗi trong dấu ngoặc kép ("") lớn hơn kích thước buf thì nó sẽ báo lỗi bus.


1
Heh ... nếu đây là trường hợp, bạn sẽ có mối quan tâm về lỗi Bus thay vì khai thác ngăn xếp mà bạn đọc mọi lúc cho Windows và các máy khác. Lỗi BUS là do cố gắng truy cập vào "bộ nhớ" mà máy chỉ đơn giản là không thể truy cập do địa chỉ không hợp lệ. .
Svartalf
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.