I thought I'd ask before spending more hours on this. Valgrind doesn't report anything, in fact it doesn't crash with Valgrind.
char* a = "HI";
char* b = strdup(a);
(gdb) print b =>
$8 = 0xffffffffe8003680 <Address 0xffffffffe8003680 out of bounds>
This only happens in my dynamically loaded shared library, (loaded with dlopen). I have no idea what could possibly cause this. I stripped just about everything out of that library, there are only these two lines in it. Can you help me debug this?
If I try to access b
now:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff76b3d0a in strchrnul () from /lib64/libc.so.6
What could possibly be going wrong here?
Backtrace:
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
You need to add
#include <string.h>
to get the declaration of strdup()
.
Or, if you already have that, you need to invoke your compiler in a way that makes strdup()
visible (see below for details). strdup()
is defined by POSIX, not by ISO C.
gcc enables the appropriate macro(s) by default, but using -ansi
or -std=c99
will disable them. You can also add an appropriate #define
to the top of your source file.
In the absence of a visible declaration, the compiler assumes (under C90 rules) that strdup()
returns int
. This results in undefined behavior. In particular, if int
is 32 bits and char*
is 64 bits, Bad Things Will Happen. (Under C99 rules, calling a function with no visible declaration is a constraint violation.)
You should also crank up your compiler's warning levels and pay attention to the warnings. A problem like this should manifest at compile time; you shouldn't have to diagnose the run-time behavior.
UPDATE :
The following is based on documentation and experiments on my own Ubuntu 11.04 system. It's likely to apply to any system using glibc; some of it might apply to non-glibc systems. man strdup
and man feature_test_macros
for more information.
To use strdup()
, you must have #include <string.h>
to make the declaration visible. (The compiler won't necessarily complain if you omit this, but you need it anyway.)
In addition, you need to have one of the following (or equivalent) before the #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 */
The simplest way to do this is just not to use the -ansi
or -std=...
option for gcc (or the equivalent for clang); gcc enables some of these macros by default.
If you want to compile with -ansi
or std=c99
, then you can explicitly set one of the above macros (or two if you use the 4th alternative), either with an explicit #define
in the source (probably the best solution), or by using, for example, gcc -std=c99 -D_XOPEN_SOURCE=500
.
And, as I mentioned before, you should crank up the warning levels on your compiler. For gcc, I typically use:
gcc -std=c99 -pedantic -Wall -Wextra -O3
The -O3
enables optimizations; a side effect is that it enables the analysis necessary to perform those optimizations, which can detect a number of problems that aren't apparent at lower optimization levels.
The reason for all this rigmarole is that strdup()
is not defined by the ANSI/ISO C standard (the committee chose not to include it), but it is defined by POSIX -- and its declaration is in <string.h>
, one of the ISO C standard headers. In strict ISO C conforming mode, the name strdup()
may not be made visible in <string.h>
. You have to take these extra steps to tell the compiler to make it visible anyway. (gcc is not a fully conforming C compiler by default, which is why it's able to make strdup()
visible by default.)
Since strdup()
is such a simple function, you might consider just writing your own implementation in standard ISO C and using that instead. For example (note that names starting with str
are reserved):
char *dupstr(const char *s) {
char *const result = malloc(strlen(s) + 1);
if (result != NULL) {
strcpy(result, s);
}
return result;
}
(I have only minimally tested this.) If strdup()
is the only POSIX-specific function you're using, rolling your own might be the best solution. If you're using other POSIX-specific functions, then you'll have to deal with all this stuff anyway, and you might as well use strdup()
itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With