Strdup返回地址超出范围

时间:2011-12-02 17:08:55

标签: c

我以为我会先花更多时间讨论这件事。 Valgrind没有报告任何事情,事实上它并没有与Valgrind一起崩溃。

char* a = "HI";
char* b = strdup(a);

(gdb)print b =&gt; $8 = 0xffffffffe8003680 <Address 0xffffffffe8003680 out of bounds>

这只发生在我动态加载的共享库中(用dlopen加载)。我不知道什么可能导致这种情况。我剥离了该库中的所有内容,其中只有这两行。你能帮我调试吗?

如果我现在尝试访问b

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff76b3d0a in strchrnul () from /lib64/libc.so.6

这里可能出现什么问题?

回溯:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff76b3d0a in strchrnul () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff76b3d0a in strchrnul () from /lib64/libc.so.6
#1  0x00007ffff767088a in vfprintf () from /lib64/libc.so.6
#2  0x00007ffff767af79 in printf () from /lib64/libc.so.6
#3  0x00007ffff722678c in _mkp_stage_30 (plugin=0x61f420, cs=0x7fffe8002040, 
    sr=0x7fffe8003070) at hello.c:68
#4  0x000000000040f93a in mk_plugin_stage_run (hook=16, socket=18, conx=0x0, 
    cs=0x7fffe8002040, sr=0x7fffe8003070) at mk_plugin.c:558
#5  0x000000000040c501 in mk_http_init (cs=0x7fffe8002040, sr=0x7fffe8003070)
    at mk_http.c:255
#6  0x0000000000404870 in mk_request_process (cs=0x7fffe8002040, 
    sr=0x7fffe8003070) at mk_request.c:510
#7  0x0000000000404d75 in mk_handler_write (socket=18, cs=0x7fffe8002040)
    at mk_request.c:630
#8  0x000000000040b446 in mk_conn_write (socket=18) at mk_connection.c:130
#9  0x0000000000409352 in mk_epoll_init (efd=12, handler=0x7fffe8001690, 
    max_events=202) at mk_epoll.c:102
#10 0x0000000000409b4e in mk_sched_launch_worker_loop (thread_conf=0x61a5a0)
    at mk_scheduler.c:196
#11 0x00007ffff79c2f05 in start_thread () from /lib64/libpthread.so.0
#12 0x00007ffff770553d in clone () from /lib64/libc.so.6

2 个答案:

答案 0 :(得分:11)

您需要添加

#include <string.h>

获取strdup()的声明。

或者,如果您已经拥有它,则需要以使strdup()可见的方式调用编译器(有关详细信息,请参见下文)。 strdup()由POSIX定义,而不是由ISO C定义。

gcc默认启用相应的宏,但使用-ansi-std=c99将禁用它们。您还可以在源文件的顶部添加适当的#define

在没有可见声明的情况下,编译器假定(根据C90规则)strdup()返回int。这导致未定义的行为。特别是,如果int是32位而char*是64位,那么坏事就会发生。 (根据C99规则,调用没有可见声明的函数是违反约束的。)

您还应该提高编译器的警告级别,注意警告。这样的问题应该在编译时出现;你不应该诊断运行时行为。

更新:

以下内容基于我自己的Ubuntu 11.04系统上的文档和实验。它可能适用于任何使用glibc的系统;其中一些可能适用于非glibc系统。 man strdupman feature_test_macros了解详情。

要使用strdup(),您必须拥有#include <string.h>才能使声明可见。 (如果省略这个,编译器不一定会抱怨,但无论如何你都需要它。)

此外,您需要在 #include <string.h>

之前使用以下(或等效的)之一
#define _SVID_SOURCE

#define _BSD_SOURCE

#define _XOPEN_SOURCE 500 /* or greater */

#define _XOPEN_SOURCE
#define _XOPEN_SOURCE_EXTENDED

#define _POSIX_C_SOURCE 200809L /* or greater */

执行此操作的最简单方法是 not 使用gcc的-ansi-std=...选项(或clang的等效选项); gcc默认启用其中一些宏。

如果您想使用-ansistd=c99进行编译,那么您可以使用显式{{1}显式设置上述宏之一(如果您使用第4个备用,则为两个)在源代码中(可能是最佳解决方案),或者使用例如#define

,正如我之前提到的,你应该提高编译器的警告级别。对于gcc,我通常使用:

gcc -std=c99 -D_XOPEN_SOURCE=500

gcc -std=c99 -pedantic -Wall -Wextra -O3 启用优化;副作用是它能够进行必要的分析以执行那些优化,这可以检测在较低优化级别下不明显的许多问题。

所有这些严重性的原因是-O3未由ANSI / ISO C标准定义(委员会选择不包括它),但它由POSIX定义 - 其声明在{ {1}},ISO C标准标头之一。在严格的ISO C合规模式下,strdup()中可能无法显示名称<string.h>。你必须采取这些额外的步骤告诉编译器无论如何都要使它可见。 (默认情况下,gcc不是完全符合标准的C编译器,这就是为什么默认情况下它可以使strdup()可见。)

由于<string.h>是一个如此简单的函数,您可以考虑在标准ISO C中编写自己的实现并使用它。例如(请注意,保留以strdup()开头的名称):

strdup()

(我只对其进行了最低限度的测试。)如果str是您正在使用的唯一POSIX特定功能,那么滚动自己可能是最佳解决方案。如果您正在使用其他POSIX特定功能,那么无论如何您都必须处理所有这些内容,您也可以使用char *dupstr(const char *s) { char *const result = malloc(strlen(s) + 1); if (result != NULL) { strcpy(result, s); } return result; } 本身。

答案 1 :(得分:1)

看起来你错误地调用了printf()。而不是printf(b),你需要做:

printf("%s\n", b);

编辑:在上面添加\ n,感谢Jonathan。