asio_guy
asio_guy

Reputation: 3767

segfaults with strtok_r I'm missing something here

here is a snippet of my program

#include <stdio.h>
#include <string.h>
void  something_wrong_really(char *str)
{
        char *savedptr = NULL;
        char *delim    = " ";

        for ( char *p = str ; ; p = NULL) {
                char *token = strtok_r(p, delim, &savedptr);
                if (token == NULL)
                        break;
                printf(" %s\n", token);
        }
}

int main(void) {
    char str[] = "Okay so lets split this and see how it works";
    something_wrong_really(str);
    return 0;
}

based on the strtok_r manual

strtok_r(): _POSIX_C_SOURCE || / Glibc versions <= 2.19: / _BSD_SOURCE || _SVID_SOURCE

so if I compile my program as

cc t.c -std=c99

I end up getting warning

t.c: In function 'something_wrong_really':
t.c:10:3: warning: implicit declaration of function 'strtok_r' [-Wimplicit-function-declaration]
   char *token = strtok_r(p, delim, &savedptr);
   ^
t.c:10:17: warning: initialization makes pointer from integer without a cast [enabled by default]
   char *token = strtok_r(p, delim, &savedptr);
                 ^

whats worse on execution it segfaults

./a.out
Segmentation fault

corresponding trace

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a5af19 in vfprintf () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-222.el7.x86_64
(gdb) bt
#0  0x00007ffff7a5af19 in vfprintf () from /lib64/libc.so.6
#1  0x00007ffff7a61339 in printf () from /lib64/libc.so.6
#2  0x00000000004005e2 in something_wrong_really (str=0x7fffffffe0a0 "Okay") at t.c:13
#3  0x0000000000400653 in main () at t.c:19
(gdb)

on the other hand thing seem to work perfectly fine when enabled one of these flags

 _SVID_SOURCE || _BSD_SOURCE || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

corresponding output

# cc t.c -std=c99 -D_BSD_SOURCE
#
#
# ./a.out
 Okay
 so
 lets
 split
 this
 and
 see
 how
 it
 works
#

any hints on why this behavior ?


so it seems the snippet in strtok_r manual

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

   strtok_r(): _POSIX_C_SOURCE
       || /* Glibc versions <= 2.19: */ _BSD_SOURCE || _SVID_SOURCE

is explained (very loosely) here feature_test_macros

The || means that in order to obtain the declaration of acct(2) from , either of the following macro definitions must be made before including any header files:

       #define _BSD_SOURCE
       #define _XOPEN_SOURCE        /* or any value < 500 */

   Alternatively, equivalent definitions can be included in the compila‐
   tion command:

       cc -D_BSD_SOURCE
       cc -D_XOPEN_SOURCE           # Or any value < 500

so as @Some programmer dude pointed out inclusion of those MACRO definitions is a must else it leads to UB which eventually is what happened here.

Upvotes: 1

Views: 300

Answers (1)

Some programmer dude
Some programmer dude

Reputation: 409176

If you do not use the correct enabling macro (_POSIX_C_SOURCE according to the manual page) then the function will not be automatically declared. That means the compiler must deduce the argument types, and more importantly the return type will automatically be int (as noted in the second warning message).

If the argument types or the return type are wrong, then that call will lead to undefined behavior, and very likely crashes.

By adding the correct macros, the function will be properly declared in the header file, and the correct types for arguments and returned value will be used.


The problem here is probably the mismatched return type. On a 64-bit system pointers (like e.g. char *) are 64 bits wide, while int is usually only 32 bits wide. This mismatch in sizes will cause many pointers to be wrong, and attempting to use them is a great source of segmentation faults.

Upvotes: 3

Related Questions