C 总线错误与分段故障

C 总线错误与分段故障,c,segmentation-fault,bus-error,sigbus,C,Segmentation Fault,Bus Error,Sigbus,总线错误和分段错误之间的区别? 程序是否会出现seg故障并第一次停止,第二次可能出现总线错误并退出?我假设您所说的是Posix定义的SIGSEGV和SIGBUS信号 SIGSEGV在程序引用无效地址时发生SIGBUS是一种实现定义的硬件故障。这两个信号的默认操作是终止程序 程序可以捕获这些信号,甚至可以忽略它们。例如,当程序尝试执行硬件总线不支持的操作时,可能会导致总线错误。例如,在On上,尝试从奇数地址读取多字节值(如int,32位)会产生总线错误 例如,当您执行违反分段规则的访问时,即尝试读

总线错误和分段错误之间的区别?
程序是否会出现seg故障并第一次停止,第二次可能出现总线错误并退出?

我假设您所说的是Posix定义的
SIGSEGV
SIGBUS
信号

SIGSEGV
在程序引用无效地址时发生<代码>SIGBUS是一种实现定义的硬件故障。这两个信号的默认操作是终止程序


程序可以捕获这些信号,甚至可以忽略它们。

例如,当程序尝试执行硬件总线不支持的操作时,可能会导致总线错误。例如,在On上,尝试从奇数地址读取多字节值(如int,32位)会产生总线错误


例如,当您执行违反分段规则的访问时,即尝试读取或写入您不拥有的内存时,就会发生分段错误。

在我使用的大多数体系结构上,区别在于:

  • 当您访问不打算访问的内存时(例如,在您的地址空间之外),会导致SEGV
  • SIGBUS是由于与CPU的对齐问题引起的(例如,试图从不是4的倍数的地址中读取长数据)
    • 如果不是因为

      程序是否会出现seg故障并第一次停止,第二次可能出现总线错误并退出

      问题的一部分。你应该能够用这里的信息自己回答这个问题


      疯狂:一次又一次地做同样的事情,期望得到不同的结果。
      --爱因斯坦


      当然,从字面上理解这个问题

      #include <signal.h>
      #include <stdlib.h>
      #include <time.h>
      #include <unistd.h>
      int main() {
          srand(time(NULL));
          if (rand() % 2)
              kill(getpid(), SIGBUS);
          else
              kill(getpid(), SIGSEGV);
          return 0;
      }
      
      #包括
      #包括
      #包括
      #包括
      int main(){
      srand(时间(空));
      如果(rand()%2)
      kill(getpid(),SIGBUS);
      其他的
      kill(getpid(),SIGSEGV);
      返回0;
      }
      

      Tada,一个程序,可以在一次运行中以分段错误退出,在另一次运行中以总线错误退出。

      将您的问题(可能不正确)解释为“我间歇性地得到SIGSEGV或SIGBUS,为什么不一致?”,值得注意的是,用指针做一些不可靠的事情并不是由C或C++标准保证的,导致了一个分段错误;这只是一种“未定义的行为”,我曾经作为一名教授说过,这意味着它可能会导致鳄鱼从地板上爬出来吃掉你

      因此,您的情况可能是您有两个bug,第一个bug有时会导致SIGSEGV,第二个bug(如果SEGVAULT没有发生并且程序仍在运行)会导致SIGBUS


      我建议您使用调试器进行调试,并注意鳄鱼。如果您创建文件并试图访问扩展到文件末尾的部分映射缓冲区,以及空间不足等错误情况,也会引发SIGBUS。如果使用注册信号处理程序并设置
      SA_SIGINFO
      ,则程序可能会检查出错的内存地址,并仅处理内存映射文件错误

      程序是否会出现seg故障并第一次停止,第二次可能出现总线错误并退出

      是的,即使是同一个bug:这里有一个来自macOS的严重但简单的例子,它可以通过数组边界之外的索引以确定性的方式同时产生分段错误(SIGSEGV)和总线错误(SIGBUS)。上述未对齐的访问不是macOS的问题。(本例不会导致任何SIGBUS,如果它在调试器中运行,在我的例子中,
      lldb

      总线segv.c:

      #include <stdlib.h>
      
      char array[10];
      
      int main(int argc, char *argv[]) {
          return array[atol(argv[1])];
      }
      
      如果查看反汇编代码,您会发现有总线错误的范围的边界并不像索引显示的那样奇怪:

      $otool-电视总线\u segv

      bus_segv:
      (__TEXT,__text) section
      _main:
      0000000100000f60    pushq   %rbp
      0000000100000f61    movq    %rsp, %rbp
      0000000100000f64    subq    $0x10, %rsp
      0000000100000f68    movl    $0x0, -0x4(%rbp)
      0000000100000f6f    movl    %edi, -0x8(%rbp)
      0000000100000f72    movq    %rsi, -0x10(%rbp)
      0000000100000f76    movq    -0x10(%rbp), %rsi
      0000000100000f7a    movq    0x8(%rsi), %rdi
      0000000100000f7e    callq   0x100000f94 ## symbol stub for: _atol
      0000000100000f83    leaq    0x96(%rip), %rsi
      0000000100000f8a    movsbl  (%rsi,%rax), %eax
      0000000100000f8e    addq    $0x10, %rsp
      0000000100000f92    popq    %rbp    
      0000000100000f93    retq    
      
      通过
      leaq 0x96(%rip),%rsi
      ,rsi成为(相对而言 已确定)阵列起始地址的地址:

      rsi = 0x100000f8a + 0x96 = 0x100001020
      rsi - 4128 = 0x100000000 (below segmentation fault)
      rsi + 24544 = 0x100007000 (here and above bus error)
      rsi + 28640 = 0x100008000 (below bus error)
      rsi + 45024 = 0x10000c000 (here and above bus error)
      rsi + 53216 = 0x10000e000 (below bus error)
      rsi + 69600 = 0x100012000 (here and above bus error)
      rsi + 73696 = 0x100013000 (below bus error)
      rsi + 77792 = 0x100014000 (here and above segmentation fault)
      
      lldb
      可能会设置具有不同页面限制的流程。我无法在调试会话中重现任何总线错误。因此,调试器可能是总线错误吐出二进制文件的解决方法


      Andreas

      当你说“读或写你不拥有的内存”是什么意思?当你做malloc时,你已经分配了5字节的内存。如果您不拥有读/写内存,它不会在C中给您一个Seg错误。相反,在同一地址空间中覆盖其他对象拥有的内存会给您一个分段错误??“您拥有的”在操作系统级别上通常比运行时显示的(例如通过malloc)要多得多。因此,有很多您拥有但仍然不应该访问的内存空间,还有很多您可以读取但不能写入的地址空间(大多数映射库),以及写保护内存区域(mprotect)的特定功能。@Geek:操作系统无法知道“谁”在同一地址空间中进行写操作。因此,它不能保护您不被同一程序内的内存覆盖。这就是为什么大多数安全漏洞都有效。与Pax和Bastien相比,我显然很差劲但是,是的,@Thunderboltz,正如其他评论员(和P&B)解释的那样,当你试图访问不属于你的内存时,会发生segfaults。内存映射文件也会生成SIGBUS。如果你从一个不是4字节对齐的地址读取浮点,就会发生在arm上的SIGBUS,我很确定我的第二个要点涵盖了这一点。可能是重复的
      rsi = 0x100000f8a + 0x96 = 0x100001020
      rsi - 4128 = 0x100000000 (below segmentation fault)
      rsi + 24544 = 0x100007000 (here and above bus error)
      rsi + 28640 = 0x100008000 (below bus error)
      rsi + 45024 = 0x10000c000 (here and above bus error)
      rsi + 53216 = 0x10000e000 (below bus error)
      rsi + 69600 = 0x100012000 (here and above bus error)
      rsi + 73696 = 0x100013000 (below bus error)
      rsi + 77792 = 0x100014000 (here and above segmentation fault)