我在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-&gt; th_buf.magic是tar_header的结果,定义:
struct tar_header{
...;
char magic[6];
char version[2];
...;
}
我非常确定strncpy可以这样使用。
在我的情况下,t-&gt; 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",
...
答案 0 :(得分:1)
您正在尝试复制8个字节:
strncpy(t->th_buf.magic, "ustar ", 8);
进入缓冲区,分配空间为6
char magic[6];
不能工作。
答案 1 :(得分:1)
magic
只有6个字符,而你正在写8个字符。
答案 2 :(得分:1)
虽然您尝试将八个字节复制到只有六个空间的缓冲区中,但这通常不会成为问题。
它通常不是问题的原因是紧随其后有一个双字节字符数组完全能够缓冲区溢出:
struct tar_header{
...;
char magic[6];
char version[2]; // Can absorb extra two chars easily.
...;
}
strncpy
的真正问题在于,大多数人都不了解它是如何运作的。
如果要复制的缓冲区大于您指定的大小,它基本上会以大小停止并且不再复制,包括字符串末尾的空终止符。
因此strncpy
最终会给你一些不的C字符串,如果你随后尝试将它用作C字符串(例如将其传递给{{1}所有地狱都可能会破裂。
这就是为什么我更喜欢使用strlen
检查后跟strlen
,假设数据在两部分之间无法改变。这样,您保证最终得到一个C字符串(或者您事先知道它)。
但是,这也不是具体问题。
在你的调用堆栈中,调用了一个检查函数strcpy
,如下所示:
__strncpy_chk
因此它正在检查,提前源字符串对于您指定的长度是否太长,并且在它甚至尝试复制之前失败。这基本上是我之前提到的相同步骤,但是强制崩溃而不是你在自己的代码中知道它。
这就是您的错误来自的地方,运行时库中的一些额外安全性。
请注意,此额外检查仅限于debug code,而不是production ready code。这样,你就可以确认你在开发过程中做了正确的事情而不会减慢你在现场的代码。