SIGSEGV (Segmentation fault) when passing a structure to callback in C

I got some strange behavior of my code. I can`t pass a structure to the callback function.

#include <malloc.h>
#include "stdint.h"

typedef struct {
    int test_rssi;
    int setup_rssi;
} test_setup_scan_result;

typedef void (*WifiCallback)(uint8_t);

WifiCallback TestSetupCb;

void resume_configuration(test_setup_scan_result *result) {
    if (result == NULL) {
        printf("result is NULL\n");
        return;
    }

    printf("result is not NULL\n");
    printf("setup_rssi %d\n", result->setup_rssi);
    printf("test_rssi %d\n", result->test_rssi);
    printf("done");
}

void scan_test_setup_done_cb() {
    printf("scan_done\n");
    test_setup_scan_result *scan_result = malloc(sizeof(test_setup_scan_result));
    printf("created structure\n");
    scan_result->test_rssi = 1;
    scan_result->setup_rssi = 1;
    printf("filled structure\n");
    if (TestSetupCb) {
        printf("executing cb\n");
        printf("setup_rssi %d\n", scan_result->setup_rssi);
        printf("test_rssi %d\n", scan_result->test_rssi);
        TestSetupCb((uint8_t) scan_result);
    }
}

void scan_for_test_setup(WifiCallback cb) {
    TestSetupCb = cb;
    scan_test_setup_done_cb();
}

int main(void) {
    printf("Hello World\n");
    scan_for_test_setup((WifiCallback)resume_configuration);
    return 0;
}

The code after I am trying to get values of the structure in resume_configuration function just not works. I am the newbie in c language so any links to useful articles are regarded.

3 answers

  • answered 2018-03-13 21:33 Paul Stelian

    I have run it in Valgrind and I have identified a blatant error (which manifests in several places):

    resume_configuration does not obey the WifiCallback pointer type; it takes a parameter of pointer type, not of uint8_t type which most likely has a different size (and it indeed has)

    You call TestSetupCb((uint8_t) scan_result); -- you explicitly convert the scan_result pointer to a datatype smaller than a pointer. You have truncated a pointer, so now the result cannot be converted back into a valid pointer. The truncation on Intel systems is so bad, any resulting pointer will always be in the NULL page.

  • answered 2018-03-13 21:34 dasblinkenlight

    Casting a function name to a function pointer type with incompatible parameter types is undefined behavior:

    scan_for_test_setup((WifiCallback)resume_configuration);
    //                  ^^^^^^^^^^^^^^
    //               Undefined behavior
    

    This by itself is enough to get a crash. However, your program does another thing illegally - the call below

    TestSetupCb((uint8_t) scan_result);
    //                    ^^^^^^^^^^^
    //               Does not fit in uint8_t
    

    would fail regardless of the function pointer cast, because a pointer does not fit in 8 bits.

  • answered 2018-03-14 17:13 Gena

    The variable “scan_result” is pointer to struct with allocated memory space in heap region. When the TestSetupCb() is called with argument “(uint8_t) scan_result” you actually casting address of “scan_result” to be uint8_t, which means to take a first byte of the address and pass it by value. In that case you lose data that you want process in resume_configuration() function. For example: let’s say the “scan_result” struct it at address “0x004EE388” when you casting this address to uint8_t the result is “0x00000088” this address is invalid and creates issue in your program. Also, this kind of operation creates dangerous to your program. To solve your problem just re-define function pointer in the following way: typedef void(*WifiCallback)( test_setup_scan_result *); and update your code: instead of TestSetupCb((uint8_t)scan_result); change to TestSetupCb(scan_result);