“总线错误”消息是什么意思?它与分段错误有何不同?
“总线错误”消息是什么意思?它与分段错误有何不同?
总线错误在x86上现在很少见,当处理器无法尝试所请求的内存访问时发生;通常是由于:
段错误发生在访问不属于进程内存的内存时。它们非常普遍,通常是以下情况的结果:
附注:更精确地说,并不是操作指针本身会导致问题,而是访问它指向的内存(解引用)。
void *
参数强制转换为存储回调的对象(一个属性指向对象,另一个属性指向该方法)。然后调用回调。但是,作为 void *
传递的东西完全不同,因此方法调用导致总线错误。 - Christopher K.段错误(segfault)是指尝试访问未授权访问的内存。这是只读的,你没有权限,等等...
总线错误(bus error)是指试图访问不可能存在的内存。您使用了系统无法理解的地址或者该操作的错误类型的地址。
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
在 Ubuntu 14.04 中测试。
POSIX 描述 SIGBUS
为:
访问内存对象的未定义部分。
mmap 规范 表示:
pa 开始并持续 len 字节的地址范围内对整个对象之后的整页的引用将导致发送 SIGBUS 信号。
shm_open
表示 它生成大小为 0 的对象:
共享内存对象大小为零。
因此,在 *map = 0
处,我们正在触及已分配对象的末尾。
ARMv8 aarch64 中非对齐的堆栈内存访问
这在 SPARC 中有提到:什么是总线错误?,但这里我将提供一个更可重现的例子。
你只需要一个 freestanding 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,Linux kernel 4.15.0下,在ThunderX2服务器上引发SIGBUS。
不幸的是,我无法在QEMU v4.0.0用户模式下重现它,我不确定原因。
该故障似乎是可选的,并由SCTLR_ELx.SA
和SCTLR_EL1.SA0
字段控制,我在此处进一步概括了相关文档。
我认为当应用程序在数据总线上表现出数据未对齐时,内核会引发SIGBUS信号。我认为由于现代大多数处理器的大多数编译器都会为程序员填充/对齐数据,因此昔日的对齐问题已经得到缓解,因此我们今天不太经常看到SIGBUS(据我所知)。
来自:这里
我同意以上所有答案。关于BUS错误,以下是我的两分钱:
BUS错误不一定来自程序代码内的指令。当您运行一个二进制文件时,在执行期间,该二进制文件被修改(被构建覆盖或删除等)可能会导致此问题。
验证是否为此情况
检查导致此错误的方法之一是从构建输出目录启动几个相同二进制文件的实例,并在它们开始后运行一个构建。 构建完成并替换了当前正在运行的二进制文件(两个实例都在运行的那个),这两个运行实例都会在短时间内崩溃并显示SIGBUS
错误。
潜在原因
这是因为操作系统交换存储页面,并且在某些情况下,该二进制文件可能没有完全加载到内存中。 当操作系统尝试从同一二进制文件获取下一页时,如果自上次读取以来二进制文件已更改,则会发生这些崩溃。
我最近在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
将第二个参数附加到第一个参数上,通过改变第一个参数来实现(反转参数也可以正常工作)。在Linux上,这会导致分段错误(如预期的那样),但在OS X上,它会导致总线错误。为什么?我真的不知道。
"foo"
存储在只读的内存段中,因此无法对其进行写入。这不是堆栈溢出保护,而是内存写入保护(如果您的程序可以重写自身,则存在安全漏洞)。 - Mark Lakata总线错误的一个典型示例是在某些体系结构上(例如SPARC,至少某些SPARC,可能已经更改),当您进行未对齐的访问时。例如:
unsigned char data[6];
(unsigned int *) (data + 2) = 0xdeadf00d;
0xdeadf00d
写入一个(很可能)未正确对齐的地址,这将在那些在这方面“挑剔”的架构上生成总线错误。顺便说一下,英特尔x86不是这样的架构。它会允许访问(尽管执行速度较慢)。/*
* SIGSEGV si_codes
*/
#define SEGV_MAPERR 1 /* address not mapped to object */
#define SEGV_ACCERR 2 /* invalid permissions for mapped object */
#define SEGV_BNDERR 3 /* failed address bound checks */
#ifdef __ia64__
# define __SEGV_PSTKOVF 4 /* paragraph stack overflow */
#else
# define SEGV_PKUERR 4 /* failed protection key checks */
#endif
#define SEGV_ACCADI 5 /* ADI not enabled for mapped object */
#define SEGV_ADIDERR 6 /* Disrupting MCD error */
#define SEGV_ADIPERR 7 /* Precise MCD exception */
#define SEGV_MTEAERR 8 /* Asynchronous ARM MTE error */
#define SEGV_MTESERR 9 /* Synchronous ARM MTE exception */
#define NSIGSEGV 9
/*
* SIGBUS si_codes
*/
#define BUS_ADRALN 1 /* invalid address alignment */
#define BUS_ADRERR 2 /* non-existent physical address */
#define BUS_OBJERR 3 /* object specific hardware error */
/* hardware memory error consumed on a machine check: action required */
#define BUS_MCEERR_AR 4
/* hardware memory error detected in process but not consumed: action optional*/
#define BUS_MCEERR_AO 5
#define NSIGBUS 5
/*
* si_code values
* Digital reserves positive values for kernel-generated signals.
*/
#define SI_USER 0 /* sent by kill, sigsend, raise */
#define SI_KERNEL 0x80 /* sent by the kernel from somewhere */
#define SI_QUEUE -1 /* sent by sigqueue */
#define SI_TIMER -2 /* sent by timer expiration */
#define SI_MESGQ -3 /* sent by real time mesq state change */
#define SI_ASYNCIO -4 /* sent by AIO completion */
#define SI_SIGIO -5 /* sent by queued SIGIO */
#define SI_TKILL -6 /* sent by tkill system call */
#define SI_DETHREAD -7 /* sent by execve() killing subsidiary threads */
#define SI_ASYNCNL -60 /* sent by glibc async name lookup completion */
#define SI_FROMUSER(siptr) ((siptr)->si_code <= 0)
#define SI_FROMKERNEL(siptr) ((siptr)->si_code > 0)
当根目录占满100%时,我遇到了总线错误。