首先,我要为无法将问题范围缩小到足以分享一个重现错误的简短程序而道歉。
对strncpy
的外部库(FUSE)调用"随机"覆盖我代码中的shared_ptr<mutex>
,当我试图锁定该互斥锁时,这会导致段错误。我用valgrind运行我的程序,它没有捕捉到任何内存错误(下面是valgrind标志)。当我在gdb中运行代码并在shared_ptr
上设置观察点时,它会在调用strncpy时中断。gdb说所有的strncpy
的参数(dest, src和nbytes)已经"优化出来",这让我认为它使用未初始化的内存调用。我的理解对吗?知道原因是什么吗?
下面是指针被覆盖时gdb的堆栈跟踪:
#0 __strncpy_ssse3 () at ../sysdeps/x86_64/multiarch/strcpy-ssse3.S:2482
#1 0x0000003245809094 in strncpy (__len=<optimized out>, __src=<optimized out>, __dest=<optimized out>) at /usr/include/bits/string3.h:120
#2 add_name (buf=<optimized out>, bufsize=<optimized out>, s=<optimized out>, name=<optimized out>) at fuse.c:907
#3 0x000000324580997c in try_get_path (f=<optimized out>, nodeid=<optimized out>, name=<optimized out>, path=<optimized out>, wnodep=<optimized out>, need_lock=<optimized out>) at fuse.c:956
#4 0x000000324580a281 in get_path_common (f=<optimized out>, nodeid=<optimized out>, name=<optimized out>, path=<optimized out>, wnode=<optimized out>) at fuse.c:1152
#5 0x0000003245812432 in fuse_lib_unlink (req=<optimized out>, parent=<optimized out>, name=<optimized out>) at fuse.c:1198
#6 0x0000003245817057 in fuse_ll_process_buf (data=0x6f5650, buf=0x7fffffffd850, ch=<optimized out>) at fuse_lowlevel.c:2441
#7 0x000000324581388f in fuse_session_loop (se=0x6f8410) at fuse_loop.c:40
#8 0x000000324580b698 in fuse_loop (f=<optimized out>) at fuse.c:4309
#9 0x000000324581bb8f in fuse_main_common (argc=<optimized out>, argv=<optimized out>, op=<optimized out>, op_size=<optimized out>, user_data=<optimized out>, compat=<optimized out>) at helper.c:355
#10 0x000000000046f1b6 in main (argc=4, argv=0x7fffffffdec8) at ../src/fuse.cpp:100
下面是我运行valgrind的参数:
valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes
这个bug是由于使用new
创建了一个shared_ptr
,然后将其类型转换为weak_ptr
并删除了它。
gdb说所有
strncpy
的参数(dest, src和nbytes)已经"优化出来",这让我认为它使用未初始化的内存进行调用。我的理解对吗?
。这仅仅意味着优化器删除了一些函数序言样板文件,这些样板文件将允许调试器可靠地打印函数的参数。这在任何合理优化级别的简单函数中都很常见。
下面是一个简单的例子:
int
mystrcpy(char *p, const char *q)
{
while (*p++ = *q++);
}
用-g -O0
编译,你会看到它立即将参数泄露到堆栈中:
mystrcpy:
.LFB0:
.file 1 "t.c"
.loc 1 5 0
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
movq %rsp, %rbp
.cfi_offset 6, -16
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp) <<< here
movq %rsi, -16(%rbp) <<< and here
...
然后编译器生成相应的调试信息:
.section .debug_info
...
.uleb128 0x7
.string "p" <<< from char *p
.byte 0x1
.byte 0x4
.long 0x65
.byte 0x2
.byte 0x91
.sleb128 -24 <<< not 100% sure but this is probably related to frame offset
.uleb128 0x7
.string "q"
.byte 0x1
.byte 0x4
.long 0x72
.byte 0x2
.byte 0x91
.sleb128 -32 <<< note adjacent to p
当你打开-O2 -g
时,尽管-g
函数变得小得多:
mystrcpy:
.LFB11:
.file 1 "t.c"
.loc 1 5 0
.cfi_startproc
(at this point we start copying, using the input regs directly)
现在在调试信息中没有对p
或q
的引用。正如您从这个示例中看到的,您可能可以在寄存器中找到所需的信息,但是您必须反汇编并理解函数才能解释它们。如果你的函数调用了其他函数,你可能不得不找到你的寄存器溢出到堆栈的地方(对于calee - saved regs来说可能相当远)。