我以前认为x86-64支持未对齐的内存访问和无效的内存访问总是导致分段错误(可能除了像movdqa或movaps之类的SIMD指令).不过最近我用普通的mov指令观察到了总线错误.这是一个复制者:
void test(void *a)
{
asm("mov %0, %%rbp\n\t"
"mov 0(%%rbp), %%rdx\n\t"
: : "r"(a) : "rbp", "rdx");
}
int main()
{
test((void *)0x706a2e3630332d69);
return 0;
}
(必须用帧指针省略编译,例如gcc -O test.c&& ./a.out).
mov 0(%rbp),%rdx指令和地址0x706a2e3630332d69是从有缺陷的程序的coredump复制的.将其更改为0会导致段错误,但只是对齐到0x706a2e3630332d60仍然是总线错误(我的猜测是它与地址空间在x86-64上的48位相关).
问题是:哪些地址导致总线错误(SIGBUS)?它是由体系结构确定还是由OS内核配置(即在页表,控制寄存器或类似的东西中)?
解决方法:
SIGBUS处于悲伤状态.不同的操作系统之间没有达成共识,它的意义和生成时间在操作系统,CPU架构,配置和月相之间变化很大.除非您使用非常具体的配置,否则您应该“将其视为SIGSEGV,但不同”.
我怀疑它本来应该意味着“你尝试过无论内核做什么都无法成功的内存访问”,所以换句话说,你在地址中的确切位模式永远不会是有效的内存访问.最常见的是,这意味着严格对齐架构上的未对齐访问.然后,一些系统开始使用它来访问不存在的虚拟地址空间(例如,在您的示例中,您所拥有的地址不可存在).然后偶然的一些系统使得它也意味着userland试图触摸内核内存(因为从技术上讲,它至少是从用户区的角度来看不存在的虚拟地址空间).然后它变得随机.
除此之外,我见过SIGBUS:
>从mmap:ed硬件访问不存在的物理地址.
> exec of non-exec mapping
>访问完全有效的映射,但此时过度使用的内存不能出错(我在这里看过SIGSEGV,SIGKILL和SIGBUS,至少有一个操作系统根据你所使用的架构有不同的做法).
>内存管理死锁(和其他“一些可怕的错误,但我们不知道什么”内存管理错误).
>堆栈红区访问
>硬件错误(ECC内存,pci总线奇偶校验错误等)
>访问mmap:ed文件,其中文件内容不存在(超过文件末尾或空洞).
>访问mmap:ed文件,其中文件内容应该存在,但不存在(I / O错误).
>无法执行换出和交换的正常内存访问(I / O错误).
标签:c-3,linux,assembly,x86
来源: https://codeday.me/bug/20190713/1453501.html