c - 无效的读/写会导致 SIGBUS 错误吗?
问题描述
编辑 1:示例程序的平台是 x86_64。
编辑2:我正在编辑这个以便更好地理解。下面是两个不同的问题。第一个问题是无效的读/写会导致 SIGBUS 吗?第二个问题是 Valgrind 对 SIGBUS 分析有用吗?示例代码用于第二个问题,以支持我的观点,即 Valgrind 在 SIGBUS 错误的情况下根本没有用。我在这里可能错了。
实际场景:我们有一个屏幕阅读器应用程序在连续测试 2 天后崩溃(由于 SIGBUS 曾经崩溃)。我有一个 coredump 文件,但我没有正确的二进制和调试包。所以基本上我必须在不同的二进制文件中测试它,并且由于调试包不匹配,coredump 在 gdb 中无法正常工作。在 Valgrind 分析期间,我可以在屏幕阅读器模块中看到一些无效的读/写。我的队友建议通过修复这些无效的读/写来解决这个问题,但我认为它不会解决它。以下是我对这两个信号的理解。
SIGSEGV:地址有效,但没有读/写权限。
SIGBUS:地址本身无效(CPU由于未对齐等原因无法找到地址)
我有一个与 SIGBUS 信号有关的问题。我已经搜索了有关堆栈溢出的类似问题,但没有找到任何明确的答案。
无效的读/写会导致总线错误(SIGBUS)吗?.
我的理解是无效的读/写总是会导致分段错误(SIGSEGV),修复总线错误的最佳方法是在应用程序上运行 gdb。在总线错误的情况下进行 Valgrind 分析根本没有帮助。下面的代码更详细地解释了这一点。
#include<stdlib.h>
#include<stdio.h>
typedef struct {
char *name;
int val;
}data;
void fun1()
{
data *ptr = malloc(sizeof(data));
ptr->val = 100;
ptr->name = "name in structure";
printf("val:%d name:%s\n",ptr->val,ptr->name);
free(ptr);
ptr = NULL;
printf("val:%d name:%s\n",ptr->val,ptr->name); //SIGSEGV
return;
}
int fun2()
{
#if defined(__GNUC__)
# if defined(__i386__)
/* Enable Alignment Checking on x86 */
__asm__("pushf\norl $0x40000,(%esp)\npopf");
# elif defined(__x86_64__)
/* Enable Alignment Checking on x86_64 */
__asm__("pushf\norl $0x40000,(%rsp)\npopf");
# endif
#endif
char *cptr = malloc(sizeof(int) + 1);
char *optr = cptr;
int *iptr = (int *) ++cptr;
*iptr = 42; //SIGBUS
free(optr);
return 0;
}
void fun()
{
fun2();
//fun1();
}
int main()
{
fun();
return 0;
}
在分段错误的情况下,Valgrind 报告将包含有关导致崩溃的代码的详细信息,但在 SIGBUS 崩溃的情况下,我在 Valgrind 报告中没有找到任何此类详细信息。
SIGSEGV 的 Valgrind 报告:
==28128== Memcheck, a memory error detector
==28128== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28128== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28128== Command: ./a.out
==28128== Parent PID: 27953
==28128==
==28128== Invalid read of size 8
==28128== at 0x400619: fun1 (tmp.c:18)
==28128== by 0x400695: fun (tmp.c:46)
==28128== by 0x4006A6: main (tmp.c:51)
==28128== Address 0x0 is not stack'd, malloc'd or (recently) free'd
==28128==
==28128==
==28128== Process terminating with default action of signal 11 (SIGSEGV)
==28128== Access not within mapped region at address 0x0
==28128== at 0x400619: fun1 (tmp.c:18)
==28128== by 0x400695: fun (tmp.c:46)
==28128== by 0x4006A6: main (tmp.c:51)
==28128== If you believe this happened as a result of a stack
==28128== overflow in your program's main thread (unlikely but
==28128== possible), you can try to increase the size of the
==28128== main thread stack using the --main-stacksize= flag.
==28128== The main thread stack size used in this run was 8388608.
==28128==
==28128== HEAP SUMMARY:
==28128== in use at exit: 0 bytes in 0 blocks
==28128== total heap usage: 2 allocs, 2 frees, 1,040 bytes allocated
==28128==
==28128== All heap blocks were freed -- no leaks are possible
==28128==
==28128== For counts of detected and suppressed errors, rerun with: -v
==28128== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
SIGBUS 的 Valgrind 报告:
==28176== Memcheck, a memory error detector
==28176== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28176== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==28176== Command: ./a.out
==28176== Parent PID: 27953
==28176==
==28176==
==28176== HEAP SUMMARY:
==28176== in use at exit: 0 bytes in 0 blocks
==28176== total heap usage: 1 allocs, 1 frees, 5 bytes allocated
==28176==
==28176== All heap blocks were freed -- no leaks are possible
==28176==
==28176== For counts of detected and suppressed errors, rerun with: -v
==28176== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
解决方案
int *iptr = (int *) ++cptr;
*iptr = 42; //SIGBUS
违反了 C 标准的多个部分。
您与6.3.2.3 Pointers的第 7 段发生冲突:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用的类型,则行为未定义。
以及违反6.5 Expressions第 7 段的严格混叠规则:
对象的存储值只能由具有以下类型之一的左值表达式访问:
- 与对象的有效类型兼容的类型,
- 与对象的有效类型兼容的类型的限定版本,
- 与对象的有效类型相对应的有符号或无符号类型,
- 对应于对象有效类型的限定版本的有符号或无符号类型,
- 聚合或联合类型,在其成员中包括上述类型之一(递归地包括子聚合或包含联合的成员),或
- 一种字符类型。
4.1。概述
Memcheck 是一个内存错误检测器。它可以检测以下 C 和 C++ 程序中常见的问题。
访问您不应该访问的内存,例如溢出和未运行堆块、溢出堆栈顶部以及在内存被释放后访问内存。
使用未定义的值,即尚未初始化的值,或从其他未定义的值派生的值。
不正确地释放堆内存,例如双重释放堆块,或 malloc/new/new[] 与 free/delete/delete[] 的不匹配使用
在 memcpy 和相关函数中重叠 src 和 dst 指针。
将可疑(可能为负)值传递给内存分配函数的大小参数。
内存泄漏。
请注意,您的代码
int *iptr = (int *) ++cptr;
*iptr = 42; //SIGBUS
Valgrind声称检测到的事情都没有做。您没有访问您无权访问的内存,也没有访问您创建的区域范围之外的内存malloc()
。你还没有free()
记忆。你没有未初始化的变量,你没有加倍free()
内存,也没有memcpy()
不正确地使用重叠的源和目标区域。而且您不会将负数/“可疑”大小传递给分配函数。而且你没有泄漏任何内存。
所以,不,Valgrind 甚至没有声称能够检测到会导致SIGBUS
.
推荐阅读
- docker - jmxterm:Docker 容器内的“无法创建系统终端”
- java - 使用 Spring Boot LaunchScript 时如何获取当前工作目录
- swift - TextEditor 更改时 UI 不刷新
- html - 更改文本的字体和颜色
- python-3.x - Exec 不会使用在 exec 中定义的变量更新当前命名空间
- python - 在 groupby 函数之后合并两个不同大小的数据帧
- jestjs - 设置 jest 的 moduleNameMapper 时可以使用通配符吗?
- java - 使用 Android 调用 PHP 函数
- linux - 如何在 Manjaro Deepin 上添加守护程序 bash 命令以启动
- postman - HMAC 保护的 API 和 Postman 请求