0

我在 encode.c:33 中跟踪 coredump。

源代码如下:

if (t->options & TAR_GNU) 
    strncpy(t->th_buf.magic, "ustar  ", 8);    // here is the coredump                                                                                                                                 
else
    .... 

函数调用栈是这样的:

0x4064d73a in strncpy (__len=8, __src=0x40663b34 "ustar  ", __dest=0x4104e5ed "") at /usr/include/i386-linux-gnu/bits/string3.h:121
0x4024e342 in __strncpy_chk () from /lib/i386-linux-gnu/libc.so.6
0x4024ee1a in __chk_fail () from /lib/i386-linux-gnu/libc.so.6
0x40250065 in __fortify_fail () from /lib/i386-linux-gnu/libc.so.6
0x401b739a in ?? () from /lib/i386-linux-gnu/libc.so.6
0x4017d825 in abort () from /lib/i386-linux-gnu/libc.so.6
0x4017a1df in raise () from /lib/i386-linux-gnu/libc.so.6
0x40064424 in __kernel_vsyscall ()
<signal handler called>
sig_coredump (sig=6) at mpm_common.c:1207

t->th_buf.magic 是 tar_header 的结构,定义:

struct tar_header{
    ...;
    char magic[6];
    char version[2];
    ...;
}

我很确定 strncpy 可以以这种方式使用。

就我而言, t->th_buf 已经有 malloc 了。

在 gdb 中:

(gdb) p t->th_buf
$5 = {name = "/TARFILE.C", '\000' <repeats 89 times>, mode = "100644 ", 
      uid = "    41", gid = "    41 ", size = "     207114 ", mtime = "12115070475 ", 
      chksum = "\000\000\000\000\000\000\000", typeflag = 48 '0', linkname = '\000' <repeats 99 times>, 
      magic = "\000\000\000\000\000", version = "\000",
      ...
4

3 回答 3

1

您正在尝试复制 8 个字节:

strncpy(t->th_buf.magic, "ustar  ", 8); 

进入一个为 6 分配空间的缓冲区

char magic[6];

不去上班。

于 2013-04-16T02:59:08.037 回答
1

magic只有 6 个字符,而您正在向其写入 8 个字符。

于 2013-04-16T02:59:10.317 回答
1

尽管您试图将 8 个字节复制到一个只能容纳 6 个字节的缓冲区中,但这通常不会成为问题。

它通常不是问题的原因是紧随其后的两字节字符数组完全能够处理缓冲区溢出:

struct tar_header{
    ...;
    char magic[6];
    char version[2];     // Can absorb extra two chars easily.
    ...;
}

真正的问题strncpy是大多数人不明白它是如何工作的。

如果要复制的缓冲区大于您指定的大小,它基本上会停止在该大小处并且不再复制,包括字符串末尾的空终止符。

因此strncpy最终可能会给你一些不是C 字符串的东西,如果你随后尝试将它用作 C 字符串(例如将它传递给strlen),那么所有的地狱都可能会崩溃。

这就是为什么我更喜欢使用strlencheck 后跟strcpy,假设数据不能在两个部分之间更改。这样,您就可以保证得到一个 C 字符串(或者您事先知道它)。


但是,这也不是这里的具体问题。

在您的调用堆栈中,有一个对检查函数的调用,__strncpy_chk如下所示:

char *
__strncpy_chk (s1, s2, n, s1len)
     char *s1;
     const char *s2;
     size_t n;
     size_t s1len;
{
    char c;
    char *s = s1;

    if (__builtin_expect (s1len < n, 0))
        __chk_fail ();
    :
    rest of function to copy the stuff.
}

因此,它会提前检查源字符串对于您指定的长度是否太长,并且在尝试复制之前就失败了。这基本上是在执行我之前提到的相同步骤,但会强制崩溃,而不是您在自己的代码中知道它。

这就是您的错误的来源,运行时库中的一些额外安全性。

请记住,这个额外的检查只在调试代码中,而不是在生产就绪代码中。这样,您可以确认您在开发过程中所做的事情是正确的,而不会减慢您在现场的代码。

于 2013-04-16T03:12:55.617 回答