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

Setting counting_illegal_callback may hide failing tests #1167

Closed
jonasnick opened this issue Dec 1, 2022 · 4 comments · Fixed by #1390
Closed

Setting counting_illegal_callback may hide failing tests #1167

jonasnick opened this issue Dec 1, 2022 · 4 comments · Fixed by #1390

Comments

@jonasnick
Copy link
Contributor

The following code snippet very roughly resembles what happens in the libsecp test harness and illustrates the issue:

/* in the tests we have access to a global context */
static secp256k1_context *ctx = NULL;

void test_foo() {
  int ecount;
  context_set_error_callback(ctx, counting_illegal_callback_fn, &ecount);
  /* do some tests with ecount */
}
void test_bar() {
  /* we don't set the counting_illegal_callback here because we don't want to test that here */
  some_function(ctx);
}
void main() {
  test_foo();
  test_bar();
}

The code is fine, until one day some_function(ctx) results in the illegal callback being called.
Then we'd want the test to fail but instead what happens is that some stack region formerly known as ecount is modified, which does not necessarily result in a crash.

One solution would be to never add the counting_illegal_callback to the global context and instead create a local context for counting.

@real-or-random
Copy link
Contributor

One solution would be to never add the counting_illegal_callback to the global context and instead create a local context for counting.

Or make the ecount global. 🤷 I guess that's good enough for test code.

@apoelstra
Copy link
Contributor

We could write an EXPECT_ARG_CHECK_FAIL macro (or maybe something with a nicer name :)) which creates a fresh ecount variable at 0, sets the context object's error callbacks, checks the ecount is 1, then restores the old callbacks.

This would also get rid of all the ecount-accounting code, which is quite a PITA to maintain and sometimes results in huge diffs.

@sipa
Copy link
Contributor

sipa commented Dec 2, 2022

We could write an EXPECT_ARG_CHECK_FAIL macro (or maybe something with a nicer name :)) which creates a fresh ecount variable at 0, sets the context object's error callbacks, checks the ecount is 1, then restores the old callbacks.

@apoelstra That would be so much more readable!

real-or-random added a commit that referenced this issue Jan 6, 2023
39e8f0e refactor: Separate run_context_tests into static vs proper contexts (Tim Ruffing)
a4a0937 tests: Clean up and improve run_context_tests() further (Tim Ruffing)
fc90bb5 refactor: Tidy up main() (Tim Ruffing)
f32a36f tests: Don't use global context for context tests (Tim Ruffing)
ce4f936 tests: Tidy run_context_tests() by extracting functions (Tim Ruffing)
18e0db3 tests: Don't recreate global context in scratch space test (Tim Ruffing)
b198061 tests: Use global copy of secp256k1_context_static instead of clone (Tim Ruffing)

Pull request description:

  This is an improved version of some of the tidying/refactoring in #1170.

  I think it's enough to deserve a separate PR. Once this is merged, I'll get back to the actual goal of #1170 (namely, forbidding cloning and randomizing static contexts.)

  This PR is a general clean up of the context tests. A notable change is that this avoids a code smell where `run_context_tests()` would use the global `ctx` variable like a local one (i.e., create a context in it and destroy it afterwards).  After this PR, the global `ctx` is properly initialized for all the other tests, and they can decide whether they want to use it or not. Same for a global `sttc`, which is a memcpy of the static context (we need a writable copy in order to be able to set callbacks).

  Note that this touches code which is also affected by #1167 but I refrained from trying to solve this issue. The goal of this PR is simply not to worsen the situation w.r.t. #1167. We should really introduce a macro to solve #1167 but that's another PR.

ACKs for top commit:
  sipa:
    utACK 39e8f0e
  apoelstra:
    ACK 39e8f0e

Tree-SHA512: a22471758111061a062b126a52a0de24a1a311d1a0332a4ef006882379a4f3f2b00e53089e3c374bf47c4051bb10bbc6a9fdbcf6d0cd4eca15b5703590395fba
@real-or-random
Copy link
Contributor

We have CHECK_ILLEGAL and CHECK_ILLEGAL_VOID since e39d954:

secp256k1/src/tests.c

Lines 55 to 72 in c545fdc

/* TODO Use CHECK_ILLEGAL(_VOID) everywhere and get rid of the uncounting callback */
/* CHECK that expr_or_stmt calls the illegal callback of ctx exactly once
*
* For checking functions that use ARG_CHECK_VOID */
#define CHECK_ILLEGAL_VOID(ctx, expr_or_stmt) do { \
int32_t _calls_to_illegal_callback = 0; \
secp256k1_callback _saved_illegal_cb = ctx->illegal_callback; \
secp256k1_context_set_illegal_callback(ctx, \
counting_illegal_callback_fn, &_calls_to_illegal_callback); \
{ expr_or_stmt; } \
ctx->illegal_callback = _saved_illegal_cb; \
CHECK(_calls_to_illegal_callback == 1); \
} while(0);
/* CHECK that expr calls the illegal callback of ctx exactly once and that expr == 0
*
* For checking functions that use ARG_CHECK */
#define CHECK_ILLEGAL(ctx, expr) CHECK_ILLEGAL_VOID(ctx, CHECK((expr) == 0))

The "only" thing left to do is to use these everywhere (and maybe introduce a similar macro for the error callback).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants