travisco_nabisco travisco_nabisco - 1 month ago 22
C Question

Using a function pointer to pthread_create causes segfault

I am attempting to stub out pthread_create to be able to fully unit test a module. When the function pointer is called from within the test framework a segmentation fault occurs. If I debug the program using 'gdb' I am able to directly call the function pointer and it works correctly.

I am using CppUTest as the unit test framework and have compiled my object files using gcc.

This function has worked in production code prior to altering it to use a function pointer for pthread_create, so I am confident in the function in general.

Stack Trace from GDB

> Starting program:
> /home/lucid/depot/torr_linux_common_dev/main/src/Utilities/tests/testRunner
> [Thread debugging using libthread_db enabled] Using host libthread_db
> library "/lib/i386-linux-gnu/libthread_db.so.1".
>
> Program received signal SIGSEGV, Segmentation fault. 0x080660c4 in
> sys_pthreads_create () (gdb) backtrace
> #0 0x080660c4 in sys_pthreads_create ()
> #1 0x08049ee4 in th_start_thread_name (thread=0x8049e64 <TestThread>, arg=0x0, opts=0x0, name=0x0) at thr.c:177
> #2 0x08049e47 in test_ThreadTestGroup_ThreadCreateUnnamed_wrapper_c () at thr_test.c:66
> #3 0x08049223 in TEST_ThreadTestGroup_ThreadCreateUnnamed_Test::testBody
> (this=0x806cc90) at testRunner.c:21
> #4 0x0805576a in PlatformSpecificSetJmpImplementation ()
> #5 0x08053ab7 in Utest::run() ()
> #6 0x080550d5 in UtestShell::runOneTestInCurrentProcess(TestPlugin*, TestResult&) ()
> #7 0x08053645 in helperDoRunOneTestInCurrentProcess ()
> #8 0x0805576a in PlatformSpecificSetJmpImplementation ()
> #9 0x08053b8f in UtestShell::runOneTest(TestPlugin*, TestResult&) ()
> #10 0x080530ef in TestRegistry::runAllTests(TestResult&) ()
> #11 0x0804a3ef in CommandLineTestRunner::runAllTests() ()
> #12 0x0804a4e9 in CommandLineTestRunner::runAllTestsMain() ()
> #13 0x0804a628 in CommandLineTestRunner::RunAllTests(int, char const**) ()
> #14 0x08049246 in main (argc=1, argv=0xbffff244) at testRunner.c:25


If I call the function pointer from within gdb it works

(gdb) p (*sys_pthreads_create)(&thr, 0, thread, arg)
[New Thread 0xb7c01b40 (LWP 17717)]
$4 = 0


Function I am testing

#include <pthread.h>
#include "mypthreads.h"
long th_start_thread_name(TH_THREAD_FUNC thread, void *arg, th_opts *opts, const char* name)
{
pthread_t thr;
int ret, sret;
//pthread_create(opts ? &opts->thr : &thr, NULL, thread, arg);
ret = (*sys_pthreads_create)(opts ? &opts->thr : &thr, 0, thread, arg);
if (ret == 0 && name != NULL)
{
extern int pthread_setname_np(pthread_t thr, const char *name); /* Fix warning from missing prototype. */

sret = pthread_setname_np(opts ? opts->thr : thr, name);
/* pthreads says that thread names must not exceed 16, including NULL. */
if (sret != 0 && strlen(name) > 15)
{
ret = -1;
}
}
return (long)ret;
}


mypthreads.h

extern int (*sys_pthreads_create(pthread_t *, const pthread_attr_t *,
void *(*) (void*), void *));


mypthreads.c

#include <stdio.h>
#include <pthread.h>

int my_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg)
{
printf("Did you get the messsage?");
return pthread_create(thread, attr, start_routine, arg);
}


int (*sys_pthreads_create)(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg) = my_pthread_create;


Edit: Added output from gdb when I call the function pointer and it succeeds.

caf caf
Answer

The problem is that your declaration in mypthreads.h has the wrong type:

extern int (*sys_pthreads_create(pthread_t *, const pthread_attr_t *, void *(*) (void*), void *));

Due to a misplaced parantheses, the type of this symbol is a function that returns a pointer to int, but your actual sys_pthreads_create object is a pointer to a function.

This means that when you call:

ret = (*sys_pthreads_create)(opts ? &opts->thr : &thr, 0, thread, arg);

sys_pthreads_create is converted to a pointer to a function by implicitly taking the address of it, then that address is dereferenced and called. But that's not really the address of a function - it's the address of a pointer to a function! So the call jumps into the data segment where sys_pthreads_create() lives and crashes when it tries to execute the function pointer as code (or crashes due to a non-executable mapping).

There's a clue to this in the gdb output:

#0  0x080660c4 in sys_pthreads_create ()

It says that it's executing within sys_pthreads_create - but sys_pthreads_create is a variable, not a function.

The compiler would have diagnosed this for you if you had included <mypthreads.h> in mypthreads.c, because the conflicting types for sys_pthreads_create would have been visible to it (that's why you should always include the header file that declares objects in the source files that define those objects).

The correct declaration of course is the one that matches mypthreads.c:

extern int (*sys_pthreads_create)(pthread_t *thread, const pthread_attr_t *attr,
                      void *(*start_routine) (void *), void *arg);

The reason that gdb was able to call the function pointer successfully was that gdb uses the type information stored in the debugging info to determine the type of sys_pthreads_create, not the bogus information from the header file.