【问题标题】:memcpy causes strange Segmentation faultmemcpy 导致奇怪的分段错误
【发布时间】:2018-05-05 14:17:47
【问题描述】:

虽然我的 C 程序看起来很完美,但我遇到了分段错误! 以下是它的工作原理:

假设我们有一个包含 6 个 int 和 1 个 int* 的 struct Heap。 (大小为 32 字节)

struct Heap
{
  int* a;
  int  b;
  int  c;
  int  d; 
  int  e;
  bool f; // Trust me it's int
  bool g; // Trust me it's int
};

ma​​in() 中我调用:

examine(heap, enable_cloning);

examine() 所做的是:

...
    if(!enable_cloning)
    {
        Heap* my_clone = new_Heap_from_clone(heap); // A clone is being made ...
...

现在让我们跳到棘手的部分(在 new_Heap_from_clone() 内部)

Heap* new_Heap_from_clone(Heap const* the_original_or_clone_heap)
{
    Heap* heap = malloc(sizeof(Heap));
    if(heap == NULL)
        return NULL;

    memcpy(heap, the_original_or_clone_heap, sizeof(Heap)); //Happy Segmentation Fault !!!
    ...

运行 GDB 我得到:

(gdb) step
174     memcpy(heap, the_original_or_clone_heap, sizeof(the_original_or_clone_heap));
(gdb) step

Program received signal SIGSEGV, Segmentation fault.
0x0000555555554dae in new_Heap_from_clone (the_original_or_clone_heap=0xb) at ./Heap.c:174
174     memcpy(heap, the_original_or_clone_heap, sizeof(the_original_or_clone_heap));
(gdb) sizeof *heap
Undefined command: "sizeof".  Try "help".
(gdb) print sizeof *heap
$5 = 32
(gdb) print sizeof *the_original_or_clone_heap
$6 = 32

注意: 我包括了 memcpy() 获得的大小,以便我从您那里获得更宝贵的信息! 让我们看看 valgrind 对此有何评论:

==6244== Use of uninitialised value of size 8
==6244==    at 0x108DAE: new_Heap_from_clone (Heap.c:174)
==6244==    by 0x1095ED: heapSort (Heap.c:481)
==6244==    by 0x10915F: sort (Heap.c:330)
==6244==    by 0x10A897: ds_bench (main.c:90)
==6244==    by 0x10A9B4: main (main.c:106)
==6244== 
==6244== Invalid read of size 8
==6244==    at 0x108DAE: new_Heap_from_clone (Heap.c:174)
==6244==    by 0x1095ED: heapSort (Heap.c:481)
==6244==    by 0x10915F: sort (Heap.c:330)
==6244==    by 0x10A897: ds_bench (main.c:90)
==6244==    by 0x10A9B4: main (main.c:106)
==6244==  Address 0xb is not stack'd, malloc'd or (recently) free'd
==6244== 
==6244== 
==6244== Process terminating with default action of signal 11 (SIGSEGV)
==6244==  Access not within mapped region at address 0xB
==6244==    at 0x108DAE: new_Heap_from_clone (Heap.c:174)
==6244==    by 0x1095ED: heapSort (Heap.c:481)
==6244==    by 0x10915F: sort (Heap.c:330)
==6244==    by 0x10A897: ds_bench (main.c:90)
==6244==    by 0x10A9B4: main (main.c:106)
==6244==  If you believe this happened as a result of a stack
==6244==  overflow in your program's main thread (unlikely but
==6244==  possible), you can try to increase the size of the
==6244==  main thread stack using the --main-stacksize= flag.
==6244==  The main thread stack size used in this run was 12001280.
==6244== 
==6244== HEAP SUMMARY:
==6244==     in use at exit: 1,648 bytes in 31 blocks
==6244==   total heap usage: 32 allocs, 1 frees, 2,672 bytes allocated
==6244== 
==6244== LEAK SUMMARY:
==6244==    definitely lost: 0 bytes in 0 blocks
==6244==    indirectly lost: 0 bytes in 0 blocks
==6244==      possibly lost: 0 bytes in 0 blocks
==6244==    still reachable: 1,648 bytes in 31 blocks
==6244==         suppressed: 0 bytes in 0 blocks
==6244== Reachable blocks (those to which a pointer was found) are not shown.
==6244== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==6244== 
==6244== For counts of detected and suppressed errors, rerun with: -v
==6244== Use --track-origins=yes to see where uninitialised values come from
==6244== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

请记住,我以前没有使用 valgrind 的经验,但很明显:

==6244== 访问不在地址 0xB 的映射区域内

但我不认为有这样的问题...... 是否有可能(以某种方式)超出堆栈空间? 但即使在这种情况下运行 valgrind :

valgrind --leak-check=yes --main-stacksize=12000000 ./hxn 这超出了默认值 (8388608) 并得到了与您看到的相同的答案。

那么,地球上的重点是什么?

【问题讨论】:

  • 请提供minimal reproducible example。否则我们无能为力。
  • main() 中的heap 指向哪里以及它如何指向定义(和初始化)的内存?
  • 请发布实际代码,而不是拼凑在一起的代码。我可以看到这不是您的真实代码,因为您的参数是 Heap,但您没有 typedef 只有 struct Heap
  • 我的赌注是the_original_or_clone_heap 包含垃圾并且指向垃圾...
  • 注意克隆功能不会克隆结构成员*a指向的内容,只克隆控制结构,它们会共享数据。稍后,如果您 free 其中一个(及其内容),另一个将失败。

标签: c segmentation-fault memcpy


【解决方案1】:

您将一个未初始化的指针传递给new_Heap_from_clone()。 gdb 和 valgrind 都试图告诉你:

  • gdb 告诉你指针是0xb,这是gdb 中未初始化指针的经典值;和
  • valgrind 在说Use of uninitialised value of size 8 时再明显不过了。

这通过了检查,因为0xb 不为空; 0x0 是。因此,当您调用 new_Heap_from_clone(heap) 时,您一定没有使用值 null 初始化特定的 heap

使用 gdb 的回溯能力(up 在调用堆栈中上升,down 下降)找出问题的确切位置。

【讨论】:

  • 很抱歉重新发布,但 Stackoverflow 的评论代码很糟糕:你似乎是我的男人! (Y) 我传递给examine(heap, enable_cloning); 的堆已经在main() 中通过调用“构造函数”Heap* new_Heap_from_data(bool buildMaxHeap, int* data, int size) 进行了初始化。此函数执行所需的适当初始化。这是经过全面测试的代码!
  • 您无法在0xb 处初始化指针。您传入的指针尚未初始化。 gdb 中的回溯应该准确地告诉您在哪里,但是:例如,尝试使用examine(&heap, enable_cloning) 而不是examine(heap, enable_cloning)(注意与号)——除非heap 已经是指向Heap 的指针,在这种情况下问题是可能不是我们可以根据给出的信息诊断的:你必须在 gdb 或类似的东西中回溯。
  • @javase 只要你拒绝发minimal reproducible example,你就会得到猜测。
  • @javase 您已经在这个网站上工作了 5 年,所以请停止表达您对网站和提供帮助的志愿者的蔑视,并阅读您尚未完成的导览。
  • @javase 0x2A 也是指针未初始化的指示。
猜你喜欢
  • 1970-01-01
  • 2021-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多