Strdup返回的地址超出范围

在我花了更多时间之前,我想我会问。 Valgrind没有报告任何事情,事实上它并没有与Valgrind一起崩溃。

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

(gdb)print b => $8 = 0xffffffffe8003680

这只发生在我动态加载的共享库中(用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 

你需要添加

 #include  

得到strdup()的声明。

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

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

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

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

更新:

以下是基于我自己的Ubuntu 11.04系统的文档和实验。 它可能适用于任何使用glibc的系统; 其中一些可能适用于非glibc系统。 man strdupman feature_test_macros获取更多信息。

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

此外,您需要在#include 之前使用以下(或等效的)之一:

 #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 */ 

最简单的方法就是不要为gcc使用-ansi-std=...选项(或者使用clang的等价物); gcc默认启用其中一些宏。

如果你想用-ansistd=c99进行编译,那么你可以显式设置上面的一个宏(如果你使用第四个替换,则可以设置两个),或者在源代码中使用显式的#define (可能是最好的解决方案),或者通过使用例如gcc -std=c99 -D_XOPEN_SOURCE=500

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

 gcc -std=c99 -pedantic -Wall -Wextra -O3 

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

所有这些严苛的原因是strdup()没有由ANSI / ISO C标准定义(委员会选择不包括它),但是它由POSIX定义 – 并且它的声明在 , ISO C标准头之一。 在严格的ISO C一致性模式下,名称strdup()可能无法在 。 你必须采取这些额外的步骤来告诉编译器无论如何都要使它可见。 (默认情况下,gcc不是完全符合C编译器的,这就是为什么默认情况下它能够使strdup()可见。)

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

 char *dupstr(const char *s) { char *const result = malloc(strlen(s) + 1); if (result != NULL) { strcpy(result, s); } return result; } 

(我只对其进行了最低限度的测试。)如果strdup()是您正在使用的唯一POSIX特定function,那么滚动自己可能是最好的解决方案。 如果你正在使用其他特定于POSIX的函数,那么无论如何你都必须处理所有这些东西,你也可以使用strdup()本身。

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

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

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