Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash in libcriterion when mocking stdlib.h's exit function #24

Open
CamJN opened this issue Apr 22, 2021 · 3 comments
Open

Crash in libcriterion when mocking stdlib.h's exit function #24

CamJN opened this issue Apr 22, 2021 · 3 comments

Comments

@CamJN
Copy link

CamJN commented Apr 22, 2021

I'm getting a crash in libcriterion when trying to use Mimick to mock stdlib.h's exit function.

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

struct Options {
  int skip;
  pid_t pid;
  bool nulls;
};
void parseArgs(int argc, char** argv, struct Options* options) {
  perror(argv[0]);
  exit(EXIT_FAILURE);
}

mmk_mock_define(exit_mock, void, int);

struct parseArgsTestCase { int argc; char** argv; struct Options options; };

char *cr_strdup(const char *str) {
  char *ptr = cr_malloc(sizeof(char) * (strlen(str) + 1));
  if (ptr)
    strcpy(ptr, str);
  return ptr;
}

void redirect_all_std_and_mock_exit(void) {
  cr_redirect_stdout();
  cr_redirect_stderr();
  mmk_mock("exit@self", exit_mock);
}
void unmock_exit(void){
  mmk_reset(exit);
}
void free_parse_args(struct criterion_test_params *crp) {
  struct parseArgsTestCase* params = (struct parseArgsTestCase*) crp->params;
  for (size_t i = 0; i < crp->length; ++i) {
    char **strings = params[i].argv;
    for (size_t j = 0; j < params[i].argc; ++j) {
      cr_free(strings[i]);
    }
    cr_free(strings);
  }
  cr_free(params);
}

ParameterizedTestParameters(parseArgs, failure) {
  size_t nb_params = 3;
  pid_t pid = 1;
  struct parseArgsTestCase* params = cr_malloc(sizeof (struct parseArgsTestCase) * nb_params);

  params[0].argc = 1;
  params[0].argv = cr_malloc(sizeof(char*) * 1);
  params[0].argv[0] = cr_strdup("progname");

  params[1].argc = 0;
  params[1].argv = NULL;

  params[2].argc = 2;
  params[2].argv = cr_malloc(sizeof(char*) * 2);
  params[2].argv[0] = cr_strdup("progname");
  params[2].argv[1] = cr_strdup("-0");

  return cr_make_param_array(struct parseArgsTestCase, params, nb_params, free_parse_args);
}

ParameterizedTest(struct parseArgsTestCase* param, parseArgs, failure, .init = redirect_all_std_and_mock_exit, .fini = unmock_exit) {
  struct Options actual = {.skip=0, .pid=0, .nulls=true};

  parseArgs(param->argc, param->argv, &actual);

  mmk_verify(exit(mmk_eq(size_t, EXIT_FAILURE)), .times = 1);
}
PATH=/usr/local/opt/llvm/bin/:$PATH CRITERION_TEST_PATTERN='parseArgs*failure' bin/tests --debug=idle
[----] Criterion v2.3.3
[====] Running 1 test from parseArgs:
[----] parseArgs::failure: Started test has PID 60151.
[RUN ] parseArgs::failure
$ lldb -p 60151
(lldb) process attach --pid 60151
Process 60151 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
    frame #0: 0x00007fff20464462 libsystem_kernel.dylib`__pthread_kill + 10
libsystem_kernel.dylib`__pthread_kill:
->  0x7fff20464462 <+10>: jae    0x7fff2046446c            ; <+20>
    0x7fff20464464 <+12>: movq   %rax, %rdi
    0x7fff20464467 <+15>: jmp    0x7fff2045e6a1            ; cerror_nocancel
    0x7fff2046446c <+20>: retq   
Target 0: (tests) stopped.

Executable module set to "~/Developer/C++/getargv/test/bin/tests".
Architecture set to: x86_64h-apple-macosx-.
(lldb) bt all
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
  * frame #0: 0x00007fff20464462 libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fff20492610 libsystem_pthread.dylib`pthread_kill + 263
    frame #2: 0x00007fff20374ba3 libsystem_c.dylib`raise + 26
    frame #3: 0x000000010c966db4 libcriterion.3.dylib`bxfi_term_sandbox_ctx + 113
    frame #4: 0x000000010c96829d libcriterion.3.dylib`bxfi_main + 84
    frame #5: 0x00007fff204ad621 libdyld.dylib`start + 1
(lldb) c
Process 60151 resuming
Process 60151 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x00007fff204ac3ee libdyld.dylib`stack_not_16_byte_aligned_error
libdyld.dylib`stack_not_16_byte_aligned_error:
->  0x7fff204ac3ee <+0>: movdqa %xmm0, (%rsp)
    0x7fff204ac3f3 <+5>: int3   

libdyld.dylib`_dyld_fast_stub_entry:
    0x7fff204ac3f4 <+0>: pushq  %rbp
    0x7fff204ac3f5 <+1>: movq   %rsp, %rbp
Target 0: (tests) stopped.

I'm almost certainly doing something wrong, but on the off chance I'm not, here's my report.

@CamJN
Copy link
Author

CamJN commented Aug 8, 2021

Simplified the test case a bunch more:

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

mmk_mock_define(exit_mock, void, int);

void mock_exit(void) {
  mmk_mock("exit@self", exit_mock);
}
void unmock_exit(void){
  mmk_reset(exit);
}

Test(parseArgs, failure, .init = mock_exit, .fini = unmock_exit) {

  exit(EXIT_FAILURE);

  mmk_verify(exit(mmk_eq(int, EXIT_FAILURE)), .times = 1);
}

I still get a fault, but possibly not an alignment fault:

Process 89175 resuming
Process 89175 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ffeee1adf50)
    frame #0: 0x00007ffeee1adf50
Target 0: (test) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ffeee1adf50)
  * frame #0: 0x00007ffeee1adf50
    frame #1: 0x0000000101a62294 libcriterion.3.dylib`criterion_internal_test_main + 107
    frame #2: 0x0000000101a62294 libcriterion.3.dylib`criterion_internal_test_main + 107
    frame #3: 0x0000000101a54295 test`parseArgs_failure_jmp at test.c:14:1
    frame #4: 0x0000000101a619bc libcriterion.3.dylib`run_test_child + 772
    frame #5: 0x0000000101a702a3 libcriterion.3.dylib`bxfi_main + 90
    frame #6: 0x00007fff20532f3d libdyld.dylib`start + 1
    frame #7: 0x00007fff20532f3d libdyld.dylib`start + 1

@CamJN
Copy link
Author

CamJN commented Aug 8, 2021

Removing criterion, and only trying to use mimick makes it clear it is an alignment error:

#include <stdlib.h>
#include <mimick.h>

mmk_mock_define(exit_mock, void, int);

int main(void) {
  mmk_mock("exit@self", exit_mock);

  exit(EXIT_FAILURE);

  mmk_reset(exit);
  return 0;
}
lldb ./test
(lldb) target create "./test"
Current executable set to '/Users/camdennarzt/Developer/C/test/test' (x86_64).
(lldb) r
Process 77726 launched: '/Users/camdennarzt/Developer/C/test/test' (x86_64)
Process 77726 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x00007fff204fac9e libdyld.dylib`stack_not_16_byte_aligned_error
libdyld.dylib`stack_not_16_byte_aligned_error:
->  0x7fff204fac9e <+0>: movdqa %xmm0, (%rsp)
    0x7fff204faca3 <+5>: int3   
    0x7fff204faca4 <+6>: nop    
    0x7fff204faca5 <+7>: nop    
Target 0: (test) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
  * frame #0: 0x00007fff204fac9e libdyld.dylib`stack_not_16_byte_aligned_error
    frame #1: 0x00007ffeefbfef98
    frame #2: 0x0000000100001951 test`mmk_mock_create_internal + 289
    frame #3: 0x0000000100003325 test`mmkuser_exit_mock_create(tgt="\x90&", opts=(sentinel_ = 0, noabort = 0)) at test.c:4:1
    frame #4: 0x000000010014a010
    frame #5: 0x00007fff204fbf3d libdyld.dylib`start + 1
(lldb) up
frame #1: 0x00007ffeefbfef98
->  0x7ffeefbfef98: callq  0x7ffeeeafaf8c
    0x7ffeefbfef9d: jg     0x7ffeefbfef9f
    0x7ffeefbfef9f: addb   %dl, 0x19(%rcx)
    0x7ffeefbfefa2: addb   %al, (%rax)
(lldb) up
frame #2: 0x0000000100001951 test`mmk_mock_create_internal + 289
test`mmk_mock_create_internal:
->  0x100001951 <+289>: movq   %rax, 0x8(%r14)
    0x100001955 <+293>: testq  %rax, %rax
    0x100001958 <+296>: je     0x100001987               ; <+343>
    0x10000195a <+298>: testb  %r15b, %r15b
(lldb)

@CamJN
Copy link
Author

CamJN commented Aug 17, 2021

even the sample test crashes:

$ lldb mimick/build/sample/strdup/strdup_test 
(lldb) target create "mimick/build/sample/strdup/strdup_test"
Current executable set to '/Users/camdennarzt/Developer/C/test/mimick/build/sample/strdup/strdup_test' (x86_64).
(lldb) r
Process 79619 launched: '/Users/camdennarzt/Developer/C/test/mimick/build/sample/strdup/strdup_test' (x86_64)
Process 79619 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x8)
    frame #0: 0x0000000100005e9e strdup_test`mmk_mock_destroy_internal + 14
strdup_test`mmk_mock_destroy_internal:
->  0x100005e9e <+14>: movq   0x8(%rdi), %rdi
    0x100005ea2 <+18>: testq  %rdi, %rdi
    0x100005ea5 <+21>: je     0x100005ec1               ; <+49>
    0x100005eab <+27>: nopl   (%rax,%rax)
Target 0: (strdup_test) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x8)
  * frame #0: 0x0000000100005e9e strdup_test`mmk_mock_destroy_internal + 14
    frame #1: 0x00000001000050fb strdup_test`test_simple_case + 539
    frame #2: 0x00000001000052b4 strdup_test`main + 20
    frame #3: 0x00007fff204fbf3d libdyld.dylib`start + 1
    frame #4: 0x00007fff204fbf3d libdyld.dylib`start + 1
(lldb) 

ziyao233 pushed a commit to ziyao233/Mimick that referenced this issue Feb 9, 2024
Signed-off-by: Audrow Nash <audrow@openrobotics.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant