cldcmocka

Wrapped function pointer parameter changes if return value struct has too many members


I compile the following main.c with gcc main.c -Wl,--wrap=foo -lcmocka:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>

typedef struct ExampleStruct {
    int32_t foo;
    int32_t qux;
    void* bar;
} ExampleStruct;

ExampleStruct __wrap_foo(int32_t foo, int32_t qux, void* bar) {
    printf("bar after: %p\n", bar);
    return (ExampleStruct) {
        .foo = foo,
        .qux = qux,
        .bar = bar
    };
}

void test() {
    void* bar = "fef";
    printf("bar before: %p\n", bar);
    foo(1, 1, bar);
    assert_int_equal(2,2);
}

int main() {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(test),
    };

    return cmocka_run_group_tests_name("success_test", tests, NULL, NULL);
}

I get the following output:

[==========] Running 1 test(s).
[ RUN      ] test
bar before: 0x40087f
bar after: 0x40087f
[       OK ] test
[==========] 1 test(s) run.
[  PASSED  ] 1 test(s).

But if I add a new member baz to the ExampleStruct:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>

typedef struct ExampleStruct {
    int32_t foo;
    int32_t qux;
    int32_t baz;
    void* bar;
} ExampleStruct;

ExampleStruct __wrap_foo(int32_t foo, int32_t qux, int32_t baz, void* bar) {
    printf("bar after: %p\n", bar);
    return (ExampleStruct) {
        .foo = foo,
        .qux = qux,
        .baz = baz,
        .bar = bar
    };
}

void test() {
    void* bar = "fef";
    printf("bar before: %p\n", bar);
    foo(1, 1, 1, bar);
    assert_int_equal(2,2);
}

int main() {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(test),
    };

    return cmocka_run_group_tests_name("success_test", tests, NULL, NULL);
}

I get the following output:

[==========] Running 1 test(s).
[ RUN      ] test
bar before: 0x40086f
bar after: 0x7f3ff17cb2cd
[  ERROR   ] --- Test failed with exception: Segmentation fault(11)
[  FAILED  ] test
[==========] 1 test(s) run.
[  PASSED  ] 0 test(s).
[  FAILED  ] 1 test(s), listed below:
[  FAILED  ] test

If I don't use a wrapped function, everything seems to work as expected.

More Details

I'm running gcc 4.8.5 with GNU ld version 2.27-44

Question

Why would adding another member to the ExampleStruct cause a segmentation fault? And why would bar change value when copied into __wrap_foo's parameters?


Solution

  • You need to add the declaration for the foo function. Currently foo(1, 1, 1, bar); will use an implicit declaration of int foo(). Implicit declarations are invalid since C99 so you should be able to turn on more warnings to catch this (-Wimplicit-function-declaration -Werror).

    The implicitly declared function takes the arguments you've given it (which will actually match __wrap_foo, given that int32_t is a typedef for int) and will return int, not ExampleStruct, so your program will have Undefined Behavior.

    Example:

    #include <assert.h>
    #include <stdio.h>
    #include <stdint.h>
    
    typedef struct ExampleStruct {
        int32_t foo;
        int32_t qux;
        int32_t baz;
        void* bar;
    } ExampleStruct;
    
    // ** add this declaration **
    ExampleStruct foo(int32_t foo, int32_t qux, int32_t baz, void* bar);
    
    ExampleStruct __wrap_foo(int32_t foo, int32_t qux, int32_t baz, void* bar) {
        printf("bar after : %p\n", bar);
        return (ExampleStruct) {
            .foo = foo,
            .qux = qux,
            .baz = baz,
            .bar = bar
        };
    }
    
    int main() {
        void* bar = "fef";
        printf("bar before: %p\n", bar);
        foo(1, 1, 1, bar);
        assert(2 == 2);
    }