Skip to content

Setting pointers to NULL inside parametrized test free function leads to segfault with -j > 1 #565

@RastislavTuranyi

Description

@RastislavTuranyi

Hello! I have been trying out Criterion for a new C library, but I have run into a perplexing issue - not sure whether i'm doing something wrong or whether this is a bug, so any help would be much appreciated!

The crux of my issue (minimal code example below) is that if I have a struct that contains pointers to other structs etc. (e.g. binary tree or linked list), and I have a custom function for freeing the entire structure that assigns each pointer to NULL after each free (or cr_free as the case may be), but if I use this freeing function in the callback for cr_make_param_array, the test segfaults when run with -j2 or greater, but runs fine with -j1.

Here is the smallest code i managed to reduce the problem into:

#include <criterion/criterion.h>
#include <criterion/parameterized.h>


struct Bar {
  int val;
};


struct Foo {
  struct Bar *bar;
};


struct Parameters {
  struct Foo *expected;
};


void free_foo(struct Foo **foo_ptr) {
  struct Foo *foo = *foo_ptr;
  
  cr_free(foo->bar);
  foo->bar = NULL;
  cr_free(foo);
  *foo_ptr = NULL;
}


void free_params(struct criterion_test_params *ctp) {
  for (int i = 0; i < ctp->length; i++) {
    struct Parameters *param = (struct Parameters *) ctp->params + i;

    free_foo(&(param->expected));    
  }
  cr_free(ctp->params);
}


ParameterizedTestParameters(test, test_bug_minimal) {
  struct Parameters *params = cr_malloc(sizeof(struct Parameters));

  params[0].expected = cr_malloc(sizeof(struct Foo));
  params[0].expected->bar = cr_malloc(sizeof(struct Bar));
  params[0].expected->bar->val = 42;

  return cr_make_param_array(struct Parameters, params, 1, free_params);
}


ParameterizedTest(struct Parameters *params, test, test_bug_minimal) {
  cr_log_info("%p %p", params->expected, params->expected->bar);
  
  cr_assert_eq(params->expected->bar->val, 42);  // SEGFAULT if j > 1
}

Compiling this simply with (using clang instead didn't resolve the issue)

gcc -c src/test_bug.c -o build/test_bug.o -lcriterion
gcc build/test_bug.o -o ../bin/test_bug -lcriterion

and then running with -j1 works fine:

$ ../bin/test_bug --verbose=0 -j1
[----] Criterion v2.4.1
[====] Running 1 test from test:
[RUN ] test::test_bug_minimal
[----] 0x5931c1000058 0x5931c1000078
[PASS] test::test_bug_minimal: (0.00s)
[====] Synthesis: Tested: 1 | Passing: 1 | Failing: 0 | Crashing: 0

Both of the pointers are intact inside the test function, so everything succeeds as i would expect. However, running with -j2 or greater results in a segfault:

$ ../bin/test_bug --verbose=0 -j2
[----] Criterion v2.4.1
[====] Running 1 test from test:
[RUN ] test::test_bug_minimal
[----] 0x56184e000058 (nil)
[----] src/test_bug.c:51: Unexpected signal caught below this line!
[FAIL] test::test_bug_minimal: CRASH!
[====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 1

In this case, the inner pointer got set to NULL before the test started, resulting in a segfault when accessing the fields of that struct.

I am using Ubuntu 24:04.2 on WSL 2.4:

WSL version: 2.4.13.0
Kernel version: 5.15.167.4-1
WSLg version: 1.0.65
MSRDC version: 1.2.5716
Direct3D version: 1.611.1-81528511
DXCore version: 10.0.26100.1-240331-1435.ge-release
Windows version: 10.0.22631.5039

and Criterion 2.4.1 installed via apt-get:

Package: libcriterion-dev
Status: install ok installed
Priority: optional
Section: libdevel
Installed-Size: 688
Architecture: amd64
Multi-Arch: same
Source: criterion
Version: 2.4.1-2build2

though i also tried to compile Criterion from source and it didn't help.

I have tried following the documentation and the examples as much as possible, but if i am doing something wrong, your help would be much appreciated, and if it is a bug, i am happy to provide more information. Thank you!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions