使用fstack-check时,意外的valgrind"无效写入"

fga*_*lan 5 c gcc valgrind

我有以下非常简单的C程序(test.c):

int f(int i)
{
  return i;
}

int main(int argC, char* argV[])
{
  int x = f(12);
  return 1;
}
Run Code Online (Sandbox Code Playgroud)

我已经用valgrind做了一些测试,-fstack-check在两个不同的系统中使用编译器标志.

Debian 8.6上用gcc 4.9.2和valgrind 3.10.0进行测试.

没有-fstack-check:

$ gcc test.c -o test
$ valgrind ./test
==103703== Memcheck, a memory error detector
==103703== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==103703== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==103703== Command: ./test
==103703== 
==103703== 
==103703== HEAP SUMMARY:
==103703==     in use at exit: 0 bytes in 0 blocks
==103703==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==103703== 
==103703== All heap blocks were freed -- no leaks are possible
==103703== 
==103703== For counts of detected and suppressed errors, rerun with: -v
==103703== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Run Code Online (Sandbox Code Playgroud)

-fstack-check:

$ gcc -fstack-check test.c -o test
$ valgrind ./test
==103726== Memcheck, a memory error detector
==103726== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==103726== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==103726== Command: ./test
==103726== 
==103726== 
==103726== HEAP SUMMARY:
==103726==     in use at exit: 0 bytes in 0 blocks
==103726==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==103726== 
==103726== All heap blocks were freed -- no leaks are possible
==103726== 
==103726== For counts of detected and suppressed errors, rerun with: -v
==103726== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
Run Code Online (Sandbox Code Playgroud)

CentOS 6.6中使用gcc 4.4.7和valgrind 3.8.1进行测试:

没有-fstack-check:

$ gcc test.c -o test
$ valgrind ./test
==16390== Memcheck, a memory error detector
==16390== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==16390== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==16390== Command: ./test
==16390== 
==16390== 
==16390== HEAP SUMMARY:
==16390==     in use at exit: 0 bytes in 0 blocks
==16390==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==16390== 
==16390== All heap blocks were freed -- no leaks are possible
==16390== 
==16390== For counts of detected and suppressed errors, rerun with: -v
==16390== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)
Run Code Online (Sandbox Code Playgroud)

-fstack-check:

$ gcc -fstack-check test.c -o test
$ valgrind ./test
==16441== Memcheck, a memory error detector
==16441== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==16441== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==16441== Command: ./test
==16441== 
==16441== Invalid write of size 8
==16441==    at 0x400497: main (in /home/fermin/valgrindtest/test)
==16441==  Address 0x7feffd058 is not stack'd, malloc'd or (recently) free'd
==16441== 
==16441== 
==16441== Process terminating with default action of signal 11 (SIGSEGV)
==16441==  Access not within mapped region at address 0x7FEFFD058
==16441==    at 0x400497: main (in /home/fermin/valgrindtest/test)
==16441==  If you believe this happened as a result of a stack
==16441==  overflow in your program's main thread (unlikely but
==16441==  possible), you can try to increase the size of the
==16441==  main thread stack using the --main-stacksize= flag.
==16441==  The main thread stack size used in this run was 10485760.
==16441== 
==16441== HEAP SUMMARY:
==16441==     in use at exit: 0 bytes in 0 blocks
==16441==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==16441== 
==16441== All heap blocks were freed -- no leaks are possible
==16441== 
==16441== For counts of detected and suppressed errors, rerun with: -v
==16441== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,此外,程序以分段错误结束.

总而言之,虽然在Debian中似乎-fstack-check都没问题,但在CentOS中我遇到了"无效写入"错误,我无法解释给定程序的代码.

阅读有关-fstack-check标志也许是错误跟踪是由于堆栈检查机制是分配在堆空间8个字节的x变量,但Valgrind的(由于某些原因)不能获得意识到这一点,所以它标志着它作为一个无效的写.也许这是valgrind 3.8.1中的一个限制,已经在3.10.0中得到了解决(鉴于在Debian设置中,使用3.10.0,我没有得到那个跟踪)?

另外,我似乎在valgring常见问题" -fomit-frame-pointer-fstack-check可以让堆栈跟踪雪上加霜",但他们没有提供关于原因,所以我不知道,如果问题是相关的信息太多了......

任何有助于解释这一点的帮助或提示都受到高度赞赏.

编辑:正如一条评论中所建议的那样,我包括CentOS 6.x案例的解码代码,编译-g3和使用objdump --source test.我在这里包含了与main()和f()函数对应的部分,但是可以在这里找到完整的转储.

二进制编译-fstack-check:

0000000000400474 <f>:
int f(int i)
{
  400474:   55                      push   %rbp
  400475:   48 89 e5                mov    %rsp,%rbp
  400478:   89 7d fc                mov    %edi,-0x4(%rbp)
  return i;
  40047b:   8b 45 fc                mov    -0x4(%rbp),%eax
}
  40047e:   c9                      leaveq 
  40047f:   c3                      retq   

0000000000400480 <main>:

int main(int argC, char* argV[])
{
  400480:   55                      push   %rbp
  400481:   48 89 e5                mov    %rsp,%rbp
  400484:   48 83 ec 20             sub    $0x20,%rsp
  400488:   89 7d ec                mov    %edi,-0x14(%rbp)
  40048b:   48 89 75 e0             mov    %rsi,-0x20(%rbp)
  int x = f(12);
  return 0;
  40048f:   48 8d 84 24 08 d0 ff    lea    -0x2ff8(%rsp),%rax
  400496:   ff 
  400497:   48 c7 00 00 00 00 00    movq   $0x0,(%rax)
  return i;
}

int main(int argC, char* argV[])
{
  int x = f(12);
  40049e:   bf 0c 00 00 00          mov    $0xc,%edi
  4004a3:   e8 cc ff ff ff          callq  400474 <f>
  4004a8:   89 45 fc                mov    %eax,-0x4(%rbp)
  return 0;
  4004ab:   b8 00 00 00 00          mov    $0x0,%eax
}
  4004b0:   c9                      leaveq 
  4004b1:   c3                      retq 
Run Code Online (Sandbox Code Playgroud)

二进制编译没有-fstack-check:

0000000000400474 <f>:
int f(int i)
{
  400474:   55                      push   %rbp
  400475:   48 89 e5                mov    %rsp,%rbp
  400478:   89 7d fc                mov    %edi,-0x4(%rbp)
  return i;
  40047b:   8b 45 fc                mov    -0x4(%rbp),%eax
}
  40047e:   c9                      leaveq 
  40047f:   c3                      retq   

0000000000400480 <main>:

int main(int argC, char* argV[])
{
  400480:   55                      push   %rbp
  400481:   48 89 e5                mov    %rsp,%rbp
  400484:   48 83 ec 20             sub    $0x20,%rsp
  400488:   89 7d ec                mov    %edi,-0x14(%rbp)
  40048b:   48 89 75 e0             mov    %rsi,-0x20(%rbp)
  int x = f(12);
  40048f:   bf 0c 00 00 00          mov    $0xc,%edi
  400494:   e8 db ff ff ff          callq  400474 <f>
  400499:   89 45 fc                mov    %eax,-0x4(%rbp)
  return 0;
  40049c:   b8 00 00 00 00          mov    $0x0,%eax
}
  4004a1:   c9                      leaveq 
  4004a2:   c3                      retq   
Run Code Online (Sandbox Code Playgroud)

EDIT2:我已经在CentOS 6.8中测试了最新的valgrind版本(3.13.0)并且我遇到了同样的问题.

fga*_*lan 2

我已经在我的 CentOS 6.6 系统中安装了 gcc 4.7.2(使用此过程)并使用以下命令重新完成了测试编译-fstack-check

$ /opt/centos/devtoolset-1.1/root/usr/bin/gcc --version
gcc (GCC) 4.7.2 20121015 (Red Hat 4.7.2-5)
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ /opt/centos/devtoolset-1.1/root/usr/bin/gcc -fstack-check test.c -o test
$ valgrind ./test
==19374== Memcheck, a memory error detector
==19374== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==19374== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==19374== Command: ./test
==19374== 
==19374== 
==19374== HEAP SUMMARY:
==19374==     in use at exit: 0 bytes in 0 blocks
==19374==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==19374== 
==19374== All heap blocks were freed -- no leaks are possible
==19374== 
==19374== For counts of detected and suppressed errors, rerun with: -v
==19374== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)
Run Code Online (Sandbox Code Playgroud)

请注意,现在不再显示“无效写入”错误。因此,这似乎是 gcc 编译器中的一个问题,正如@nm 在评论中建议的那样,在 4.4.7 和 4.7.2 之间修复了某种错误。