【问题标题】:This malloc shouldn't work这个 malloc 不应该工作
【发布时间】:2014-03-01 01:49:49
【问题描述】:

这是我的代码。

 int     main()
  {
  char *s;
  int i = 0;

  printf("%lu \n", sizeof(s));

  s = malloc(sizeof(char) * 2);

  printf("%lu \n", sizeof(s));
  /*Why is this working?*/
  while (i <= 5)
    {
      s[i] = 'l';
      i++;
    }
  printf("%s \n", s);

  printf("%lu \n", sizeof(char));

  printf("%lu \n", sizeof(s[0]));
  }

在我看来,这应该是段错误,因为我试图写的比分配的多。为什么会这样?

【问题讨论】:

  • 看似成功是未定义行为的一种有效形式
  • 可能只是运气...
  • 未定义行为不同于segfault。谷歌“什么是段错误”,谷歌会很高兴地告诉你,这里的用例与“访问 CPU 无法物理寻址的内存”不同
  • 为什么你期望它会给你一个错误?在大多数情况下,它会运行得很好(就像你的一样)。这取决于您在在哪里写作。
  • 旁注:在打印size_t 类型的整数时使用%zu

标签: c malloc


【解决方案1】:

正如其他人所说,代码是否会在生产中导致运行时错误,因为边界检查没有内置在 C++ 中(与 Java 或 C# 语言不同)。该代码将在内存检查器下导致错误。

您可能知道 Valgrind,所以这是留给读者的练习。在 Clang 的Address Sanitizer 下也是一样的(我加了一个printf("malloc: %p \n", s);):

$ ./t.exe | /usr/local/bin/asan_symbolize.py 
malloc: 0x60200000b3b0 
=================================================================
==98557==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000b3b2 at pc 0x1065c4b5b bp 0x7fff5963b810 sp 0x7fff5963b808
WRITE of size 1 at 0x60200000b3b2 thread T0
    #0 0x1065c4b5a (/Users/jwalton/./t.exe+0x100000b5a)
    #1 0x7fff870e27e0 (/usr/lib/system/libdyld.dylib+0x27e0)
    #2 0x0
0x60200000b3b2 is located 0 bytes to the right of 2-byte region [0x60200000b3b0,0x60200000b3b2)
allocated by thread T0 here:
    #0 0x1065d8cd5 (/usr/local/lib/clang/3.3/lib/darwin//libclang_rt.asan_osx_dynamic.dylib+0xfcd5)
    #1 0x1065c4971 (/Users/jwalton/./t.exe+0x100000971)
    #2 0x7fff870e27e0 (/usr/lib/system/libdyld.dylib+0x27e0)
    #3 0x0
Shadow bytes around the buggy address:
  0x1c0400001620: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001630: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001640: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001650: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x1c0400001660: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x1c0400001670: fa fa fa fa fa fa[02]fa fa fa 00 00 fa fa fd fa
  0x1c0400001680: fa fa fd fa fa fa 00 00 fa fa fd fa fa fa fd fa
  0x1c0400001690: fa fa 00 00 fa fa 00 00 fa fa fd fa fa fa fd fa
  0x1c04000016a0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x1c04000016b0: fa fa 00 00 fa fa fd fa fa fa fd fa fa fa 00 00
  0x1c04000016c0: fa fa 00 00 fa fa fd fa fa fa fd fa fa fa 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap right redzone:    fb
  Freed heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==98557==ABORTING

【讨论】:

    【解决方案2】:

    看起来像这样:malloc 分配的字节数比您指定的多..

    当你使用 malloc(sizeof(s)*2); //8 那么 while (i

    当你使用例如 malloc(sizeof(s)*4); //16 那么 while (i

    (我在 code::blocks 中测试过)

    丹尼斯·里奇死了太可惜了,为什么会这样仍然是个大谜 那个,但是不要太担心它,只要分配就足够了 你也需要 null 终止字符串

    【讨论】:

    • 您所说的行为与C(或Dennis Ritchie)无关。你所说的其实根本不是谜,它只是undefined behavior
    【解决方案3】:

    Segfault 是来自操作系统的信号,它告诉您访问特定内存区域与您无关。碰巧的是,您正在访问的内容不会触发操作系统内存管理单元的警报。有很多方法可以利用它(覆盖函数调用、通过覆盖堆栈值来攻击二进制文件等)。

    也可能是您的 malloc 不分配这 2 个字节和仅分配 2 个字节。 Malloc 调用分配虚拟内存页面(可能超过 2 个字节)的系统调用。该系统调用(sbrkVirtualAlloc 分别用于 Linux 和 Windows)告诉操作系统将这些页面映射到您需要的页面上,然后保护它们,以免其他人(阅读:另一个进程/应用程序)意外踩到您的内存区域(因为在这种情况下,操作系统会以一个段错误击中 那个人的脑袋)。

    还有其他人提到的未定义行为。

    【讨论】:

    • 这是一个很好的解释,但从技术上讲,保护内存页面免受其他进程影响的不是malloc - 这是操作系统的工作。你写它的方式听起来像是malloc谁在做这件事。
    【解决方案4】:

    在说明中 (并完全同意)@Ed S 的回答,试试这个代码示例,并添加一些额外的变量以完全相同的方式声明并在char *s 之后立即分配。

    虽然不能保证变量按顺序存储在内存中,但以这种方式创建它们的可能性很高。 如果是这样char *t 现在将拥有char *s 将侵占的空间,而您遇到段错误:

    int     main()
    {
        char *s;
        char *t;//addition
        int i = 0;
    
        printf("%lu \n", sizeof(s));
    
        s = malloc(sizeof(char) * 2);
        t = malloc(sizeof(char) * 2);//addition
    
        printf("%lu \n", sizeof(s));
        /*Why is this working?*/
        while (i <= 5)
        {
          s[i] = 'l';
          i++;
        }
        printf("%s \n", s);
    
        printf("%lu \n", sizeof(char));
    
        printf("%lu \n", sizeof(s[0]));
    }
    

    注意: 在我使用的环境中,(Windows 7、NI 运行时、调试等)无论哪种方式,我都会遇到 seg-fault,这在一定程度上支持其他答案中未定义的行为断言。

    【讨论】:

      【解决方案5】:

      在我看来,这应该是段错误,因为我试图写的比分配的多。为什么会这样?

      这不是“工作”;您的代码调用未定义的行为。 “未定义的行为”并不意味着“您的代码会出现段错误”。那将是定义的行为。 UB 意味着任何事情都可能发生。

      在这种情况下,您踩的是不属于您的内存。这有时会出现段错误,但不要指望它。 C 没有来自您的操作系统的“段错误”的概念。

      【讨论】:

      • 明确说明。不错。
      猜你喜欢
      • 2013-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-07
      • 1970-01-01
      相关资源
      最近更新 更多