Repository: bitcoin-core/secp256k1
Pull request #1169 (2022-12-06 23:54:38)

Add support for msan instead of valgrind (for memcheck and ctime test)
This introduces an abstraction layer `src/checkmem.h`, which defines macros for interacting with memory checking tools. Depending on the environment, they're mapped to MemorySanitizer builtins, Valgrind integration macros, or nothing at all.

This means that msan builds immediately benefit from existing undefined memory checks in the tests. It also means those builds result in a `ctime_tests` (new name for `valgrind_ctime_test`) binary that can usefully test constant-timeness (not inside Valgrind, and with the downside that it's not running against a production library build, but it's faster and available on more platforms).

Such an msan-ctime test is added to the Linux x86_64 msan CI job, as an example. More CI cases could be added (e.g. for MacOs or ARM Linux) later.

real-or-random: > @real-or-random There appears to be some work on a clang/mingw crossover ([mstorsjo/llvm-mingw](https://github.com/mstorsjo/llvm-mingw)), but I'm not sure it's mature enough to try (or whether it support msan; only ubsan and asan are listed).

It's probably not worth the effort. I'd expect that the differences between different LLVM OS targets are rather small, or at least in areas such as binary formats, linkage, symbols, etc. and not in actual code generation. This is even more true in our case because we don't really interact with the OS except.
hebasto: Testing 6ef1afa3ff1437d47ce21aace4409a51ebf81aa5 on Ubuntu 22.04:
```
$ ./autogen.sh
$ ./configure --enable-ctime-tests CFLAGS="-fsanitize=memory -O1" CC=clang
...
Build Options:
  with external callbacks = no
  with benchmarks         = yes
  with tests              = yes
  with ctime tests        = yes
  with coverage           = no
  with examples           = no
  module ecdh             = yes
  module recovery         = no
  module extrakeys        = yes
  module schnorrsig       = yes

  asm                     = x86_64
  ecmult window size      = 15
  ecmult gen prec. bits   = 4

  valgrind                = no
  CC                      = clang
  CPPFLAGS                =  
  SECP_CFLAGS             = -O2  -std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef -Wno-overlength-strings -Wall -Wno-unused-function -Wextra -Wcast-align -Wconditional-uninitialized -fvisibility=hidden 
  CFLAGS                  = -fsanitize=memory -O1
  LDFLAGS 
$ make clean
$ make
$ ./libtool --mode=execute ./ctime_tests 
==346524==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x7f87d91c34e2 in secp256k1_scalar_mul secp256k1.c
    #1 0x7f87d9193de5 in secp256k1_ecdsa_sign (/home/hebasto/git/secp256k1/.libs/libsecp256k1.so.1+0xbde5) (BuildId: 8a99d7711aeabba7b1dc311e74fedcef767eb76b)
    #2 0x5618435418c7 in run_tests (/home/hebasto/git/secp256k1/.libs/ctime_tests+0xa48c7) (BuildId: 013cc8d2ba72c4e385d011449343a9ea11a16616)
    #3 0x56184354145b in main (/home/hebasto/git/secp256k1/.libs/ctime_tests+0xa445b) (BuildId: 013cc8d2ba72c4e385d011449343a9ea11a16616)
    #4 0x7f87d8e66d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 69389d485a9793dbe873f0ea2c93e02efaa9aa3d)
    #5 0x7f87d8e66e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 69389d485a9793dbe873f0ea2c93e02efaa9aa3d)
    #6 0x5618434bb3a4 in _start (/home/hebasto/git/secp256k1/.libs/ctime_tests+0x1e3a4) (BuildId: 013cc8d2ba72c4e385d011449343a9ea11a16616)

SUMMARY: MemorySanitizer: use-of-uninitialized-value secp256k1.c in secp256k1_scalar_mul
Exiting
```

Is it expected?
real-or-random: @hebasto Yes, msan is not clever enough to work with our asm. Try disabling asm. 

Patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index aabfea2..ac682e5 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -23,7 +23,7 @@ env:
   SECP256K1_TEST_ITERS:
   BENCH: yes
   SECP256K1_BENCH_ITERS: 2
-  CTIMETEST: yes
+  CTIMETESTS: yes
   # Compile and run the tests
   EXAMPLES: yes
 
@@ -40,8 +40,8 @@ cat_logs_snippet: &CAT_LOGS
       - cat noverify_tests.log || true
     cat_exhaustive_tests_log_script:
       - cat exhaustive_tests.log || true
-    cat_valgrind_ctime_test_log_script:
-      - cat valgrind_ctime_test.log || true
+    cat_ctime_tests_log_script:
+      - cat ctime_tests.log || true
     cat_bench_log_script:
       - cat bench.log || true
     cat_config_log_script:
@@ -81,9 +81,9 @@ task:
     - env: {WIDEMUL: int128,                 ECDH: yes, SCHNORRSIG: yes}
     - env: {WIDEMUL: int128,  ASM: x86_64}
     - env: {                  RECOVERY: yes,            SCHNORRSIG: yes}
-    - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no}
+    - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETESTS: no, BENCH: no}
     - env: {CPPFLAGS: -DDETERMINISTIC}
-    - env: {CFLAGS: -O0, CTIMETEST: no}
+    - env: {CFLAGS: -O0, CTIMETESTS: no}
     - env: { ECMULTGENPRECISION: 2, ECMULTWINDOW: 2 }
     - env: { ECMULTGENPRECISION: 8, ECMULTWINDOW: 4 }
   matrix:
@@ -128,7 +128,7 @@ task:
   env:
     ASM: no
     WITH_VALGRIND: no
-    CTIMETEST: no
+    CTIMETESTS: no
   matrix:
     - env:
         CC: gcc
@@ -153,7 +153,7 @@ task:
     ECDH: yes
     RECOVERY: yes
     SCHNORRSIG: yes
-    CTIMETEST: no
+    CTIMETESTS: no
   << : *MERGE_BASE
   test_script:
     # https://sourceware.org/bugzilla/show_bug.cgi?id=27008
@@ -172,7 +172,7 @@ task:
     ECDH: yes
     RECOVERY: yes
     SCHNORRSIG: yes
-    CTIMETEST: no
+    CTIMETESTS: no
   matrix:
     - env: {}
     - env: {EXPERIMENTAL: yes, ASM: arm}
@@ -192,7 +192,7 @@ task:
     ECDH: yes
     RECOVERY: yes
     SCHNORRSIG: yes
-    CTIMETEST: no
+    CTIMETESTS: no
   << : *MERGE_BASE
   test_script:
     - ./ci/cirrus.sh
@@ -209,7 +209,7 @@ task:
     ECDH: yes
     RECOVERY: yes
     SCHNORRSIG: yes
-    CTIMETEST: no
+    CTIMETESTS: no
   << : *MERGE_BASE
   test_script:
     - ./ci/cirrus.sh
@@ -223,7 +223,7 @@ task:
     ECDH: yes
     RECOVERY: yes
     SCHNORRSIG: yes
-    CTIMETEST: no
+    CTIMETESTS: no
   matrix:
     - name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)"
       env:
@@ -246,7 +246,7 @@ task:
     RECOVERY: yes
     EXPERIMENTAL: yes
     SCHNORRSIG: yes
-    CTIMETEST: no
+    CTIMETESTS: no
     # Use a MinGW-w64 host to tell ./configure we're building for Windows.
     # This will detect some MinGW-w64 tools but then make will need only
     # the MSVC tools CC, AR and NM as specified below.
@@ -285,7 +285,7 @@ task:
     ECDH: yes
     RECOVERY: yes
     SCHNORRSIG: yes
-    CTIMETEST: no
+    CTIMETESTS: no
   matrix:
     - name: "Valgrind (memcheck)"
       container:
@@ -330,10 +330,11 @@ task:
     ECDH: yes
     RECOVERY: yes
     SCHNORRSIG: yes
-    CTIMETEST: no
+    CTIMETESTS: yes
     CC: clang
     SECP256K1_TEST_ITERS: 32
     ASM: no
+    WITH_VALGRIND: no
   container:
     memory: 2G
   matrix:
diff --git a/.gitignore b/.gitignore
index c68645b..6ef3e2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,7 @@ tests
 exhaustive_tests
 precompute_ecmult_gen
 precompute_ecmult
-valgrind_ctime_test
+ctime_tests
 ecdh_example
 ecdsa_example
 schnorr_example
diff --git a/Makefile.am b/Makefile.am
index 666afa5..7aefce4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -47,6 +47,7 @@ noinst_HEADERS += src/modinv64_impl.h
 noinst_HEADERS += src/precomputed_ecmult.h
 noinst_HEADERS += src/precomputed_ecmult_gen.h
 noinst_HEADERS += src/assumptions.h
+noinst_HEADERS += src/checkmem.h
 noinst_HEADERS += src/util.h
 noinst_HEADERS += src/int128.h
 noinst_HEADERS += src/int128_impl.h
@@ -96,10 +97,6 @@ libsecp256k1_la_CPPFLAGS = $(SECP_INCLUDES) $(SECP_CONFIG_DEFINES)
 libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB)
 libsecp256k1_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_CURRENT):$(LIB_VERSION_REVISION):$(LIB_VERSION_AGE)
 
-if VALGRIND_ENABLED
-libsecp256k1_la_CPPFLAGS += -DVALGRIND
-endif
-
 noinst_PROGRAMS =
 if USE_BENCHMARK
 noinst_PROGRAMS += bench bench_internal bench_ecmult
@@ -122,12 +119,6 @@ noverify_tests_SOURCES = src/tests.c
 noverify_tests_CPPFLAGS = $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) $(SECP_CONFIG_DEFINES)
 noverify_tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB)
 noverify_tests_LDFLAGS = -static
-if VALGRIND_ENABLED
-noverify_tests_CPPFLAGS += -DVALGRIND
-noinst_PROGRAMS += valgrind_ctime_test
-valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
-valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
-endif
 if !ENABLE_COVERAGE
 TESTS += tests
 noinst_PROGRAMS += tests
@@ -138,6 +129,13 @@ tests_LDFLAGS  = $(noverify_tests_LDFLAGS)
 endif
 endif
 
+if USE_CTIME_TESTS
+noinst_PROGRAMS += ctime_tests
+ctime_tests_SOURCES = src/ctime_tests.c
+ctime_tests_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
+ctime_tests_CPPFLAGS = $(SECP_CONFIG_DEFINES)
+endif
+
 if USE_EXHAUSTIVE_TESTS
 noinst_PROGRAMS += exhaustive_tests
 exhaustive_tests_SOURCES = src/tests_exhaustive.c
diff --git a/ci/cirrus.sh b/ci/cirrus.sh
index 80ce8f2..e1ca35e 100755
--- a/ci/cirrus.sh
+++ b/ci/cirrus.sh
@@ -13,7 +13,7 @@ print_environment() {
     for var in WERROR_CFLAGS MAKEFLAGS BUILD \
             ECMULTWINDOW ECMULTGENPRECISION ASM WIDEMUL WITH_VALGRIND EXTRAFLAGS \
             EXPERIMENTAL ECDH RECOVERY SCHNORRSIG \
-            SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETEST\
+            SECP256K1_TEST_ITERS BENCH SECP256K1_BENCH_ITERS CTIMETESTS\
             EXAMPLES \
             HOST WRAPPER_CMD \
             CC CFLAGS CPPFLAGS AR NM
@@ -62,6 +62,7 @@ fi
     --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
     --enable-module-schnorrsig="$SCHNORRSIG" \
     --enable-examples="$EXAMPLES" \
+    --enable-ctime-tests="$CTIMETESTS" \
     --with-valgrind="$WITH_VALGRIND" \
     --host="$HOST" $EXTRAFLAGS
 
@@ -78,14 +79,15 @@ export LOG_COMPILER="$WRAPPER_CMD"
 
 make "$BUILD"
 
+# Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
+EXEC='./libtool --mode=execute'
+if [ -n "$WRAPPER_CMD" ]
+then
+    EXEC="$EXEC $WRAPPER_CMD"
+fi
+
 if [ "$BENCH" = "yes" ]
 then
-    # Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
-    EXEC='./libtool --mode=execute'
-    if [ -n "$WRAPPER_CMD" ]
-    then
-        EXEC="$EXEC $WRAPPER_CMD"
-    fi
     {
         $EXEC ./bench_ecmult
         $EXEC ./bench_internal
@@ -93,9 +95,13 @@ then
     } >> bench.log 2>&1
 fi
 
-if [ "$CTIMETEST" = "yes" ]
+if [ "$CTIMETESTS" = "yes" ]
 then
-    ./libtool --mode=execute valgrind --error-exitcode=42 ./valgrind_ctime_test > valgrind_ctime_test.log 2>&1
+    if [ "$WITH_VALGRIND" = "yes" ]; then
+        ./libtool --mode=execute valgrind --error-exitcode=42 ./ctime_tests > ctime_tests.log 2>&1
+    else
+        $EXEC ./ctime_tests > ctime_tests.log 2>&1
+    fi
 fi
 
 # Rebuild precomputed files (if not cross-compiling).
diff --git a/configure.ac b/configure.ac
index d389a10..cb5d039 100644
--- a/configure.ac
+++ b/configure.ac
@@ -142,6 +142,10 @@ AC_ARG_ENABLE(tests,
     AS_HELP_STRING([--enable-tests],[compile tests [default=yes]]), [],
     [SECP_SET_DEFAULT([enable_tests], [yes], [yes])])
 
+AC_ARG_ENABLE(ctime_tests,
+    AS_HELP_STRING([--enable-ctime-tests],[compile constant-time tests [default=yes if valgrind enabled]]), [],
+    [SECP_SET_DEFAULT([enable_ctime_tests], [auto], [auto])])
+
 AC_ARG_ENABLE(experimental,
     AS_HELP_STRING([--enable-experimental],[allow experimental configure options [default=no]]), [],
     [SECP_SET_DEFAULT([enable_experimental], [no], [yes])])
@@ -225,7 +229,10 @@ else
     enable_valgrind=yes
   fi
 fi
-AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
+
+if test x"$enable_ctime_tests" = x"auto"; then
+    enable_ctime_tests=$enable_valgrind
+fi
 
 if test x"$enable_coverage" = x"yes"; then
     SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES -DCOVERAGE=1"
@@ -344,7 +351,7 @@ case $set_ecmult_gen_precision in
 esac
 
 if test x"$enable_valgrind" = x"yes"; then
-  SECP_INCLUDES="$SECP_INCLUDES $VALGRIND_CPPFLAGS"
+  SECP_CONFIG_DEFINES="$SECP_CONFIG_DEFINES $VALGRIND_CPPFLAGS -DVALGRIND"
 fi
 
 # Add -Werror and similar flags passed from the outside (for testing, e.g., in CI).
@@ -407,6 +414,7 @@ AC_SUBST(SECP_CFLAGS)
 AC_SUBST(SECP_CONFIG_DEFINES)
 AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"])
 AM_CONDITIONAL([USE_TESTS], [test x"$enable_tests" != x"no"])
+AM_CONDITIONAL([USE_CTIME_TESTS], [test x"$enable_ctime_tests" = x"yes"])
 AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$enable_exhaustive_tests" != x"no"])
 AM_CONDITIONAL([USE_EXAMPLES], [test x"$enable_examples" != x"no"])
 AM_CONDITIONAL([USE_BENCHMARK], [test x"$enable_benchmark" = x"yes"])
@@ -428,6 +436,7 @@ echo "Build Options:"
 echo "  with external callbacks = $enable_external_default_callbacks"
 echo "  with benchmarks         = $enable_benchmark"
 echo "  with tests              = $enable_tests"
+echo "  with ctime tests        = $enable_ctime_tests"
 echo "  with coverage           = $enable_coverage"
 echo "  with examples           = $enable_examples"
 echo "  module ecdh             = $enable_module_ecdh"
diff --git a/doc/safegcd_implementation.md b/doc/safegcd_implementation.md
index 063aa8e..5216231 100644
--- a/doc/safegcd_implementation.md
+++ b/doc/safegcd_implementation.md
@@ -410,7 +410,7 @@ sufficient even. Given that every loop iteration performs *N* divsteps, it will
 
 To deal with the branches in `divsteps_n_matrix` we will replace them with constant-time bitwise
 operations (and hope the C compiler isn't smart enough to turn them back into branches; see
-`valgrind_ctime_test.c` for automated tests that this isn't the case). To do so, observe that a
+`ctime_tests.c` for automated tests that this isn't the case). To do so, observe that a
 divstep can be written instead as (compare to the inner loop of `gcd` in section 1).
 
 ```python
diff --git a/src/checkmem.h b/src/checkmem.h
new file mode 100644
index 0000000..571e4cc
--- /dev/null
+++ b/src/checkmem.h
@@ -0,0 +1,88 @@
+/***********************************************************************
+ * Copyright (c) 2022 Pieter Wuille                                    *
+ * Distributed under the MIT software license, see the accompanying    *
+ * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
+ ***********************************************************************/
+
+/* The code here is inspired by Kris Kwiatkowski's approach in
+ * https://github.com/kriskwiatkowski/pqc/blob/main/src/common/ct_check.h
+ * to provide a general interface for memory-checking mechanisms, primarily
+ * for constant-time checking.
+ */
+
+/* These macros are defined by this header file:
+ *
+ * - SECP256K1_CHECKMEM_ENABLED:
+ *   - 1 if memory-checking integration is available, 0 otherwise.
+ *     This is just a compile-time macro. Use the next macro to check it is actually
+ *     available at runtime.
+ * - SECP256K1_CHECKMEM_RUNNING():
+ *   - Acts like a function call, returning 1 if memory checking is available
+ *     at runtime.
+ * - SECP256K1_CHECKMEM_CHECK(p, len):
+ *   - Assert or otherwise fail in case the len-byte memory block pointed to by p is
+ *     not considered entirely defined.
+ * - SECP256K1_CHECKMEM_CHECK_VERIFY(p, len):
+ *   - Like SECP256K1_CHECKMEM_CHECK, but only works in VERIFY mode.
+ * - SECP256K1_CHECKMEM_UNDEFINE(p, len):
+ *   - marks the len-byte memory block pointed to by p as undefined data (secret data,
+ *     in the context of constant-time checking).
+ * - SECP256K1_CHECKMEM_DEFINE(p, len):
+ *   - marks the len-byte memory pointed to by p as defined data (public data, in the
+ *     context of constant-time checking).
+ *
+ */
+
+#ifndef SECP256K1_CHECKMEM_H
+#define SECP256K1_CHECKMEM_H
+
+/* Define a statement-like macro that ignores the arguments. */
+#define SECP256K1_CHECKMEM_NOOP(p, len) do { (void)(p); (void)(len); } while(0)
+
+/* If compiling under msan, map the SECP256K1_CHECKMEM_* functionality to msan.
+ * Choose this preferentially, even when VALGRIND is defined, as msan-compiled
+ * binaries can't be run under valgrind anyway. */
+#if defined(__has_feature)
+#  if __has_feature(memory_sanitizer)
+#    include <sanitizer/msan_interface.h>
+#    define SECP256K1_CHECKMEM_ENABLED 1
+#    define SECP256K1_CHECKMEM_UNDEFINE(p, len) __msan_allocated_memory((p), (len))
+#    define SECP256K1_CHECKMEM_DEFINE(p, len) __msan_unpoison((p), (len))
+#    define SECP256K1_CHECKMEM_CHECK(p, len) __msan_check_mem_is_initialized((p), (len))
+#    define SECP256K1_CHECKMEM_RUNNING() (1)
+#  endif
+#endif
+
+/* If valgrind integration is desired (through the VALGRIND define), implement the
+ * SECP256K1_CHECKMEM_* macros using valgrind. */
+#if !defined SECP256K1_CHECKMEM_ENABLED
+#  if defined VALGRIND
+#    include <stddef.h>
+#    include <valgrind/memcheck.h>
+#    define SECP256K1_CHECKMEM_ENABLED 1
+#    define SECP256K1_CHECKMEM_UNDEFINE(p, len) VALGRIND_MAKE_MEM_UNDEFINED((p), (len))
+#    define SECP256K1_CHECKMEM_DEFINE(p, len) VALGRIND_MAKE_MEM_DEFINED((p), (len))
+#    define SECP256K1_CHECKMEM_CHECK(p, len) VALGRIND_CHECK_MEM_IS_DEFINED((p), (len))
+     /* VALGRIND_MAKE_MEM_DEFINED returns 0 iff not running on memcheck.
+      * This is more precise than the RUNNING_ON_VALGRIND macro, which
+      * checks for valgrind in general instead of memcheck specifically. */
+#    define SECP256K1_CHECKMEM_RUNNING() (VALGRIND_MAKE_MEM_DEFINED(NULL, 0) != 0)
+#  endif
+#endif
+
+/* As a fall-back, map these macros to dummy statements. */
+#if !defined SECP256K1_CHECKMEM_ENABLED
+#  define SECP256K1_CHECKMEM_ENABLED 0
+#  define SECP256K1_CHECKMEM_UNDEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
+#  define SECP256K1_CHECKMEM_DEFINE(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
+#  define SECP256K1_CHECKMEM_CHECK(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
+#  define SECP256K1_CHECKMEM_RUNNING() (0)
+#endif
+
+#if defined VERIFY
+#define SECP256K1_CHECKMEM_CHECK_VERIFY(p, len) SECP256K1_CHECKMEM_CHECK((p), (len))
+#else
+#define SECP256K1_CHECKMEM_CHECK_VERIFY(p, len) SECP256K1_CHECKMEM_NOOP((p), (len))
+#endif
+
+#endif /* SECP256K1_CHECKMEM_H */
diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h
index 21742bf..5e6c63c 100644
--- a/src/field_10x26_impl.h
+++ b/src/field_10x26_impl.h
@@ -7,6 +7,7 @@
 #ifndef SECP256K1_FIELD_REPR_IMPL_H
 #define SECP256K1_FIELD_REPR_IMPL_H
 
+#include "checkmem.h"
 #include "util.h"
 #include "field.h"
 #include "modinv32_impl.h"
@@ -1132,7 +1133,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
 
 static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
     uint32_t mask0, mask1;
-    VG_CHECK_VERIFY(r->n, sizeof(r->n));
+    SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n));
     mask0 = flag + ~((uint32_t)0);
     mask1 = ~mask0;
     r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
@@ -1231,7 +1232,7 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) {
 
 static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
     uint32_t mask0, mask1;
-    VG_CHECK_VERIFY(r->n, sizeof(r->n));
+    SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n));
     mask0 = flag + ~((uint32_t)0);
     mask1 = ~mask0;
     r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h
index 63487ef..985c486 100644
--- a/src/field_5x52_impl.h
+++ b/src/field_5x52_impl.h
@@ -7,6 +7,7 @@
 #ifndef SECP256K1_FIELD_REPR_IMPL_H
 #define SECP256K1_FIELD_REPR_IMPL_H
 
+#include "checkmem.h"
 #include "util.h"
 #include "field.h"
 #include "modinv64_impl.h"
@@ -472,7 +473,7 @@ static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) {
 
 static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) {
     uint64_t mask0, mask1;
-    VG_CHECK_VERIFY(r->n, sizeof(r->n));
+    SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n));
     mask0 = flag + ~((uint64_t)0);
     mask1 = ~mask0;
     r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
@@ -555,7 +556,7 @@ static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) {
 
 static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
     uint64_t mask0, mask1;
-    VG_CHECK_VERIFY(r->n, sizeof(r->n));
+    SECP256K1_CHECKMEM_CHECK_VERIFY(r->n, sizeof(r->n));
     mask0 = flag + ~((uint64_t)0);
     mask1 = ~mask0;
     r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1);
diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h
index 4588219..1b83575 100644
--- a/src/scalar_4x64_impl.h
+++ b/src/scalar_4x64_impl.h
@@ -7,6 +7,7 @@
 #ifndef SECP256K1_SCALAR_REPR_IMPL_H
 #define SECP256K1_SCALAR_REPR_IMPL_H
 
+#include "checkmem.h"
 #include "int128.h"
 #include "modinv64_impl.h"
 
@@ -810,7 +811,7 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
 
 static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
     uint64_t mask0, mask1;
-    VG_CHECK_VERIFY(r->d, sizeof(r->d));
+    SECP256K1_CHECKMEM_CHECK_VERIFY(r->d, sizeof(r->d));
     mask0 = flag + ~((uint64_t)0);
     mask1 = ~mask0;
     r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1);
diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h
index 62c7ae7..c433adc 100644
--- a/src/scalar_8x32_impl.h
+++ b/src/scalar_8x32_impl.h
@@ -7,6 +7,7 @@
 #ifndef SECP256K1_SCALAR_REPR_IMPL_H
 #define SECP256K1_SCALAR_REPR_IMPL_H
 
+#include "checkmem.h"
 #include "modinv32_impl.h"
 
 /* Limbs of the secp256k1 order. */
@@ -631,7 +632,7 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r,
 
 static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
     uint32_t mask0, mask1;
-    VG_CHECK_VERIFY(r->d, sizeof(r->d));
+    SECP256K1_CHECKMEM_CHECK_VERIFY(r->d, sizeof(r->d));
     mask0 = flag + ~((uint32_t)0);
     mask1 = ~mask0;
     r->d[0] = (r->d[0] & mask0) | (a->d[0] & mask1);
diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h
index 7176f0b..e780083 100644
--- a/src/scalar_low_impl.h
+++ b/src/scalar_low_impl.h
@@ -7,6 +7,7 @@
 #ifndef SECP256K1_SCALAR_REPR_IMPL_H
 #define SECP256K1_SCALAR_REPR_IMPL_H
 
+#include "checkmem.h"
 #include "scalar.h"
 
 #include <string.h>
@@ -115,7 +116,7 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const
 
 static SECP256K1_INLINE void secp256k1_scalar_cmov(secp256k1_scalar *r, const secp256k1_scalar *a, int flag) {
     uint32_t mask0, mask1;
-    VG_CHECK_VERIFY(r, sizeof(*r));
+    SECP256K1_CHECKMEM_CHECK_VERIFY(r, sizeof(*r));
     mask0 = flag + ~((uint32_t)0);
     mask1 = ~mask0;
     *r = (*r & mask0) | (*a & mask1);
diff --git a/src/secp256k1.c b/src/secp256k1.c
index 67c8019..6c91a76 100644
--- a/src/secp256k1.c
+++ b/src/secp256k1.c
@@ -21,6 +21,7 @@
 #include "../include/secp256k1_preallocated.h"
 
 #include "assumptions.h"
+#include "checkmem.h"
 #include "util.h"
 
 #include "field_impl.h"
@@ -40,10 +41,6 @@
 # error "secp256k1.h processed without SECP256K1_BUILD defined while building secp256k1.c"
 #endif
 
-#if defined(VALGRIND)
-# include <valgrind/memcheck.h>
-#endif
-
 #define ARG_CHECK(cond) do { \
     if (EXPECT(!(cond), 0)) { \
         secp256k1_callback_call(&ctx->illegal_callback, #cond); \
@@ -102,6 +99,12 @@ size_t secp256k1_context_preallocated_size(unsigned int flags) {
             return 0;
     }
 
+    if (EXPECT(!SECP256K1_CHECKMEM_RUNNING() && (flags & SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY), 0)) {
+            secp256k1_callback_call(&default_illegal_callback,
+                                    "Declassify flag requires running with memory checking");
+            return 0;
+    }
+
     return ret;
 }
 
@@ -215,17 +218,10 @@ void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scr
 }
 
 /* Mark memory as no-longer-secret for the purpose of analysing constant-time behaviour
- *  of the software. This is setup for use with valgrind but could be substituted with
- *  the appropriate instrumentation for other analysis tools.
+ *  of the software.
  */
 static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, const void *p, size_t len) {
-#if defined(VALGRIND)
-    if (EXPECT(ctx->declassify,0)) VALGRIND_MAKE_MEM_DEFINED(p, len);
-#else
-    (void)ctx;
-    (void)p;
-    (void)len;
-#endif
+    if (EXPECT(ctx->declassify, 0)) SECP256K1_CHECKMEM_DEFINE(p, len);
 }
 
 static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) {
diff --git a/src/util.h b/src/util.h
index e1af5a2..a25ac87 100644
--- a/src/util.h
+++ b/src/util.h
@@ -97,25 +97,6 @@ static const secp256k1_callback default_error_callback = {
 #define VERIFY_SETUP(stmt)
 #endif
 
-/* Define `VG_UNDEF` and `VG_CHECK` when VALGRIND is defined  */
-#if !defined(VG_CHECK)
-# if defined(VALGRIND)
-#  include <valgrind/memcheck.h>
-#  define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y))
-#  define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y))
-# else
-#  define VG_UNDEF(x,y)
-#  define VG_CHECK(x,y)
-# endif
-#endif
-
-/* Like `VG_CHECK` but on VERIFY only */
-#if defined(VERIFY)
-#define VG_CHECK_VERIFY(x,y) VG_CHECK((x), (y))
-#else
-#define VG_CHECK_VERIFY(x,y)
-#endif
-
 static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) {
     void *ret = malloc(size);
     if (ret == NULL) {


Test patch:
diff --git a/src/valgrind_ctime_test.c b/src/ctime_tests.c
similarity index 67%
rename from src/valgrind_ctime_test.c
rename to src/ctime_tests.c
index a0f888b..2b29568 100644
--- a/src/valgrind_ctime_test.c
+++ b/src/ctime_tests.c
@@ -4,12 +4,15 @@
  * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
  ***********************************************************************/
 
-#include <valgrind/memcheck.h>
 #include <stdio.h>
 
 #include "../include/secp256k1.h"
 #include "assumptions.h"
-#include "util.h"
+#include "checkmem.h"
+
+#if !SECP256K1_CHECKMEM_ENABLED
+#  error "This tool cannot be compiled without memory-checking interface (valgrind or msan)"
+#endif
 
 #ifdef ENABLE_MODULE_ECDH
 # include "../include/secp256k1_ecdh.h"
@@ -34,9 +37,9 @@ int main(void) {
     unsigned char key[32];
     int ret, i;
 
-    if (!RUNNING_ON_VALGRIND) {
-        fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
-        fprintf(stderr, "Usage: libtool --mode=execute valgrind ./valgrind_ctime_test\n");
+    if (!SECP256K1_CHECKMEM_RUNNING()) {
+        fprintf(stderr, "Unless compiled under msan, this test can only usefully be run inside valgrind.\n");
+        fprintf(stderr, "Usage: libtool --mode=execute valgrind ./ctime_tests\n");
         return 1;
     }
     ctx = secp256k1_context_create(SECP256K1_CONTEXT_DECLASSIFY);
@@ -51,9 +54,9 @@ int main(void) {
 
     /* Test context randomisation. Do this last because it leaves the context
      * tainted. */
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
     ret = secp256k1_context_randomize(ctx, key);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret);
 
     secp256k1_context_destroy(ctx);
@@ -83,89 +86,89 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) {
     }
 
     /* Test keygen. */
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
     ret = secp256k1_ec_pubkey_create(ctx, &pubkey, key);
-    VALGRIND_MAKE_MEM_DEFINED(&pubkey, sizeof(secp256k1_pubkey));
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret);
     CHECK(secp256k1_ec_pubkey_serialize(ctx, spubkey, &outputlen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
 
     /* Test signing. */
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
     ret = secp256k1_ecdsa_sign(ctx, &signature, msg, key, NULL, NULL);
-    VALGRIND_MAKE_MEM_DEFINED(&signature, sizeof(secp256k1_ecdsa_signature));
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&signature, sizeof(secp256k1_ecdsa_signature));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret);
     CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature));
 
 #ifdef ENABLE_MODULE_ECDH
     /* Test ECDH. */
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
     ret = secp256k1_ecdh(ctx, msg, &pubkey, key, NULL, NULL);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
 #endif
 
 #ifdef ENABLE_MODULE_RECOVERY
     /* Test signing a recoverable signature. */
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
     ret = secp256k1_ecdsa_sign_recoverable(ctx, &recoverable_signature, msg, key, NULL, NULL);
-    VALGRIND_MAKE_MEM_DEFINED(&recoverable_signature, sizeof(recoverable_signature));
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&recoverable_signature, sizeof(recoverable_signature));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret);
     CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &recoverable_signature));
     CHECK(recid >= 0 && recid <= 3);
 #endif
 
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
     ret = secp256k1_ec_seckey_verify(ctx, key);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
 
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
     ret = secp256k1_ec_seckey_negate(ctx, key);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
 
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
-    VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(msg, 32);
     ret = secp256k1_ec_seckey_tweak_add(ctx, key, msg);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
 
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
-    VALGRIND_MAKE_MEM_UNDEFINED(msg, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(msg, 32);
     ret = secp256k1_ec_seckey_tweak_mul(ctx, key, msg);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
 
     /* Test keypair_create and keypair_xonly_tweak_add. */
 #ifdef ENABLE_MODULE_EXTRAKEYS
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
     ret = secp256k1_keypair_create(ctx, &keypair, key);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
 
     /* The tweak is not treated as a secret in keypair_tweak_add */
-    VALGRIND_MAKE_MEM_DEFINED(msg, 32);
+    SECP256K1_CHECKMEM_DEFINE(msg, 32);
     ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
 
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
-    VALGRIND_MAKE_MEM_UNDEFINED(&keypair, sizeof(keypair));
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(&keypair, sizeof(keypair));
     ret = secp256k1_keypair_sec(ctx, key, &keypair);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
 #endif
 
 #ifdef ENABLE_MODULE_SCHNORRSIG
-    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    SECP256K1_CHECKMEM_UNDEFINE(key, 32);
     ret = secp256k1_keypair_create(ctx, &keypair, key);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
     ret = secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL);
-    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    SECP256K1_CHECKMEM_DEFINE(&ret, sizeof(ret));
     CHECK(ret == 1);
 #endif
 }
diff --git a/src/tests.c b/src/tests.c
index 8a1c6f8..bfdb68d 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -14,6 +14,7 @@
 #include "../include/secp256k1.h"
 #include "../include/secp256k1_preallocated.h"
 #include "testrand_impl.h"
+#include "checkmem.h"
 #include "util.h"
 
 #include "../contrib/lax_der_parsing.c"
@@ -193,14 +194,14 @@ void run_ec_illegal_argument_tests(void) {
     /* Verify context-type checking illegal-argument errors. */
     CHECK(secp256k1_ec_pubkey_create(STATIC_CTX, &pubkey, ctmp) == 0);
     CHECK(ecount == 1);
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 1);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ecdsa_sign(STATIC_CTX, &sig, ctmp, ctmp, NULL, NULL) == 0);
     CHECK(ecount == 2);
-    VG_UNDEF(&sig, sizeof(sig));
+    SECP256K1_CHECKMEM_UNDEFINE(&sig, sizeof(sig));
     CHECK(secp256k1_ecdsa_sign(CTX, &sig, ctmp, ctmp, NULL, NULL) == 1);
-    VG_CHECK(&sig, sizeof(sig));
+    SECP256K1_CHECKMEM_CHECK(&sig, sizeof(sig));
     CHECK(ecount2 == 10);
     CHECK(secp256k1_ecdsa_verify(CTX, &sig, ctmp, &pubkey) == 1);
     CHECK(ecount2 == 10);
@@ -5444,7 +5445,7 @@ void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvali
         /* Smaller sizes are tested exhaustively elsewhere. */
         int32_t i;
         memcpy(&pubkeyc[1], input, 64);
-        VG_UNDEF(&pubkeyc[pubkeyclen], 65 - pubkeyclen);
+        SECP256K1_CHECKMEM_UNDEFINE(&pubkeyc[pubkeyclen], 65 - pubkeyclen);
         for (i = 0; i < 256; i++) {
             /* Try all type bytes. */
             int xpass;
@@ -5463,14 +5464,14 @@ void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvali
                 unsigned char pubkeyo[65];
                 size_t outl;
                 memset(&pubkey, 0, sizeof(pubkey));
-                VG_UNDEF(&pubkey, sizeof(pubkey));
+                SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
                 ecount = 0;
                 CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, pubkeyclen) == 1);
-                VG_CHECK(&pubkey, sizeof(pubkey));
+                SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
                 outl = 65;
-                VG_UNDEF(pubkeyo, 65);
+                SECP256K1_CHECKMEM_UNDEFINE(pubkeyo, 65);
                 CHECK(secp256k1_ec_pubkey_serialize(CTX, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
-                VG_CHECK(pubkeyo, outl);
+                SECP256K1_CHECKMEM_CHECK(pubkeyo, outl);
                 CHECK(outl == 33);
                 CHECK(secp256k1_memcmp_var(&pubkeyo[1], &pubkeyc[1], 32) == 0);
                 CHECK((pubkeyclen != 33) || (pubkeyo[0] == pubkeyc[0]));
@@ -5479,13 +5480,13 @@ void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvali
                     CHECK(pubkeyo[0] == ysign);
                     CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 1);
                     memset(&pubkey, 0, sizeof(pubkey));
-                    VG_UNDEF(&pubkey, sizeof(pubkey));
+                    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
                     secp256k1_pubkey_save(&pubkey, &ge);
-                    VG_CHECK(&pubkey, sizeof(pubkey));
+                    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
                     outl = 65;
-                    VG_UNDEF(pubkeyo, 65);
+                    SECP256K1_CHECKMEM_UNDEFINE(pubkeyo, 65);
                     CHECK(secp256k1_ec_pubkey_serialize(CTX, pubkeyo, &outl, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1);
-                    VG_CHECK(pubkeyo, outl);
+                    SECP256K1_CHECKMEM_CHECK(pubkeyo, outl);
                     CHECK(outl == 65);
                     CHECK(pubkeyo[0] == 4);
                     CHECK(secp256k1_memcmp_var(&pubkeyo[1], input, 64) == 0);
@@ -5495,9 +5496,9 @@ void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvali
                 /* These cases must fail to parse. */
                 memset(&pubkey, 0xfe, sizeof(pubkey));
                 ecount = 0;
-                VG_UNDEF(&pubkey, sizeof(pubkey));
+                SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
                 CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, pubkeyclen) == 0);
-                VG_CHECK(&pubkey, sizeof(pubkey));
+                SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
                 CHECK(ecount == 0);
                 CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0);
                 CHECK(ecount == 1);
@@ -5696,15 +5697,15 @@ void run_ec_pubkey_parse_test(void) {
     int32_t ecount2;
     ecount = 0;
     /* Nothing should be reading this far into pubkeyc. */
-    VG_UNDEF(&pubkeyc[65], 1);
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkeyc[65], 1);
     secp256k1_context_set_illegal_callback(CTX, counting_illegal_callback_fn, &ecount);
     /* Zero length claimed, fail, zeroize, no illegal arg error. */
     memset(&pubkey, 0xfe, sizeof(pubkey));
     ecount = 0;
-    VG_UNDEF(shortkey, 2);
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(shortkey, 2);
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, shortkey, 0) == 0);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(ecount == 0);
     CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0);
     CHECK(ecount == 1);
@@ -5713,10 +5714,10 @@ void run_ec_pubkey_parse_test(void) {
         memset(&pubkey, 0xfe, sizeof(pubkey));
         ecount = 0;
         shortkey[0] = i;
-        VG_UNDEF(&shortkey[1], 1);
-        VG_UNDEF(&pubkey, sizeof(pubkey));
+        SECP256K1_CHECKMEM_UNDEFINE(&shortkey[1], 1);
+        SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
         CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, shortkey, 1) == 0);
-        VG_CHECK(&pubkey, sizeof(pubkey));
+        SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
         CHECK(ecount == 0);
         CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0);
         CHECK(ecount == 1);
@@ -5727,19 +5728,19 @@ void run_ec_pubkey_parse_test(void) {
         ecount = 0;
         shortkey[0] = i & 255;
         shortkey[1] = i >> 8;
-        VG_UNDEF(&pubkey, sizeof(pubkey));
+        SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
         CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, shortkey, 2) == 0);
-        VG_CHECK(&pubkey, sizeof(pubkey));
+        SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
         CHECK(ecount == 0);
         CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0);
         CHECK(ecount == 1);
     }
     memset(&pubkey, 0xfe, sizeof(pubkey));
     ecount = 0;
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     /* 33 bytes claimed on otherwise valid input starting with 0x04, fail, zeroize output, no illegal arg error. */
     CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 33) == 0);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(ecount == 0);
     CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0);
     CHECK(ecount == 1);
@@ -5749,43 +5750,43 @@ void run_ec_pubkey_parse_test(void) {
     /* NULL input string. Illegal arg and zeroize output. */
     memset(&pubkey, 0xfe, sizeof(pubkey));
     ecount = 0;
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, NULL, 65) == 0);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(ecount == 1);
     CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0);
     CHECK(ecount == 2);
     /* 64 bytes claimed on input starting with 0x04, fail, zeroize output, no illegal arg error. */
     memset(&pubkey, 0xfe, sizeof(pubkey));
     ecount = 0;
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 64) == 0);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(ecount == 0);
     CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0);
     CHECK(ecount == 1);
     /* 66 bytes claimed, fail, zeroize output, no illegal arg error. */
     memset(&pubkey, 0xfe, sizeof(pubkey));
     ecount = 0;
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 66) == 0);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(ecount == 0);
     CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 0);
     CHECK(ecount == 1);
     /* Valid parse. */
     memset(&pubkey, 0, sizeof(pubkey));
     ecount = 0;
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_parse(CTX, &pubkey, pubkeyc, 65) == 1);
     CHECK(secp256k1_ec_pubkey_parse(secp256k1_context_static, &pubkey, pubkeyc, 65) == 1);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(ecount == 0);
-    VG_UNDEF(&ge, sizeof(ge));
+    SECP256K1_CHECKMEM_UNDEFINE(&ge, sizeof(ge));
     CHECK(secp256k1_pubkey_load(CTX, &ge, &pubkey) == 1);
-    VG_CHECK(&ge.x, sizeof(ge.x));
-    VG_CHECK(&ge.y, sizeof(ge.y));
-    VG_CHECK(&ge.infinity, sizeof(ge.infinity));
+    SECP256K1_CHECKMEM_CHECK(&ge.x, sizeof(ge.x));
+    SECP256K1_CHECKMEM_CHECK(&ge.y, sizeof(ge.y));
+    SECP256K1_CHECKMEM_CHECK(&ge.infinity, sizeof(ge.infinity));
     ge_equals_ge(&secp256k1_ge_const_g, &ge);
     CHECK(ecount == 0);
     /* secp256k1_ec_pubkey_serialize illegal args. */
@@ -5797,9 +5798,9 @@ void run_ec_pubkey_parse_test(void) {
     CHECK(secp256k1_ec_pubkey_serialize(CTX, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0);
     CHECK(ecount == 2);
     len = 65;
-    VG_UNDEF(sout, 65);
+    SECP256K1_CHECKMEM_UNDEFINE(sout, 65);
     CHECK(secp256k1_ec_pubkey_serialize(CTX, sout, &len, NULL, SECP256K1_EC_UNCOMPRESSED) == 0);
-    VG_CHECK(sout, 65);
+    SECP256K1_CHECKMEM_CHECK(sout, 65);
     CHECK(ecount == 3);
     CHECK(len == 0);
     len = 65;
@@ -5807,9 +5808,9 @@ void run_ec_pubkey_parse_test(void) {
     CHECK(ecount == 4);
     CHECK(len == 0);
     len = 65;
-    VG_UNDEF(sout, 65);
+    SECP256K1_CHECKMEM_UNDEFINE(sout, 65);
     CHECK(secp256k1_ec_pubkey_serialize(CTX, sout, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1);
-    VG_CHECK(sout, 65);
+    SECP256K1_CHECKMEM_CHECK(sout, 65);
     CHECK(ecount == 4);
     CHECK(len == 65);
     /* Multiple illegal args. Should still set arg error only once. */
@@ -5854,33 +5855,33 @@ void run_eckey_edge_case_test(void) {
     int32_t ecount;
     /* Group order is too large, reject. */
     CHECK(secp256k1_ec_seckey_verify(CTX, orderc) == 0);
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, orderc) == 0);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
     /* Maximum value is too large, reject. */
     memset(ctmp, 255, 32);
     CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0);
     memset(&pubkey, 1, sizeof(pubkey));
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 0);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
     /* Zero is too small, reject. */
     memset(ctmp, 0, 32);
     CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0);
     memset(&pubkey, 1, sizeof(pubkey));
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 0);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
     /* One must be accepted. */
     ctmp[31] = 0x01;
     CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 1);
     memset(&pubkey, 0, sizeof(pubkey));
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 1);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
     pubkey_one = pubkey;
     /* Group order + 1 is too large, reject. */
@@ -5888,17 +5889,17 @@ void run_eckey_edge_case_test(void) {
     ctmp[31] = 0x42;
     CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 0);
     memset(&pubkey, 1, sizeof(pubkey));
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 0);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
     /* -1 must be accepted. */
     ctmp[31] = 0x40;
     CHECK(secp256k1_ec_seckey_verify(CTX, ctmp) == 1);
     memset(&pubkey, 0, sizeof(pubkey));
-    VG_UNDEF(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_ec_pubkey_create(CTX, &pubkey, ctmp) == 1);
-    VG_CHECK(&pubkey, sizeof(pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
     pubkey_negone = pubkey;
     /* Tweak of zero leaves the value unchanged. */
@@ -6030,29 +6031,29 @@ void run_eckey_edge_case_test(void) {
     /* secp256k1_ec_pubkey_combine tests. */
     ecount = 0;
     pubkeys[0] = &pubkey_one;
-    VG_UNDEF(&pubkeys[0], sizeof(secp256k1_pubkey *));
-    VG_UNDEF(&pubkeys[1], sizeof(secp256k1_pubkey *));
-    VG_UNDEF(&pubkeys[2], sizeof(secp256k1_pubkey *));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkeys[0], sizeof(secp256k1_pubkey *));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkeys[1], sizeof(secp256k1_pubkey *));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkeys[2], sizeof(secp256k1_pubkey *));
     memset(&pubkey, 255, sizeof(secp256k1_pubkey));
-    VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 0) == 0);
-    VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
     CHECK(ecount == 1);
     CHECK(secp256k1_ec_pubkey_combine(CTX, NULL, pubkeys, 1) == 0);
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
     CHECK(ecount == 2);
     memset(&pubkey, 255, sizeof(secp256k1_pubkey));
-    VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, NULL, 1) == 0);
-    VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
     CHECK(ecount == 3);
     pubkeys[0] = &pubkey_negone;
     memset(&pubkey, 255, sizeof(secp256k1_pubkey));
-    VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 1) == 1);
-    VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
     CHECK(ecount == 3);
     len = 33;
@@ -6063,17 +6064,17 @@ void run_eckey_edge_case_test(void) {
     pubkeys[0] = &pubkey_one;
     pubkeys[1] = &pubkey_negone;
     memset(&pubkey, 255, sizeof(secp256k1_pubkey));
-    VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 2) == 0);
-    VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0);
     CHECK(ecount == 3);
     /* Passes through infinity but comes out one. */
     pubkeys[2] = &pubkey_one;
     memset(&pubkey, 255, sizeof(secp256k1_pubkey));
-    VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 3) == 1);
-    VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
     CHECK(ecount == 3);
     len = 33;
@@ -6083,9 +6084,9 @@ void run_eckey_edge_case_test(void) {
     /* Adds to two. */
     pubkeys[1] = &pubkey_one;
     memset(&pubkey, 255, sizeof(secp256k1_pubkey));
-    VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_UNDEFINE(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_ec_pubkey_combine(CTX, &pubkey, pubkeys, 2) == 1);
-    VG_CHECK(&pubkey, sizeof(secp256k1_pubkey));
+    SECP256K1_CHECKMEM_CHECK(&pubkey, sizeof(secp256k1_pubkey));
     CHECK(secp256k1_memcmp_var(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0);
     CHECK(ecount == 3);
     secp256k1_context_set_illegal_callback(CTX, NULL, NULL);
@@ -7096,18 +7097,18 @@ void test_ecdsa_edge_cases(void) {
         unsigned char nonce2[32];
         unsigned char nonce3[32];
         unsigned char nonce4[32];
-        VG_UNDEF(nonce,32);
-        VG_UNDEF(nonce2,32);
-        VG_UNDEF(nonce3,32);
-        VG_UNDEF(nonce4,32);
+        SECP256K1_CHECKMEM_UNDEFINE(nonce,32);
+        SECP256K1_CHECKMEM_UNDEFINE(nonce2,32);
+        SECP256K1_CHECKMEM_UNDEFINE(nonce3,32);
+        SECP256K1_CHECKMEM_UNDEFINE(nonce4,32);
         CHECK(nonce_function_rfc6979(nonce, zeros, zeros, NULL, NULL, 0) == 1);
-        VG_CHECK(nonce,32);
+        SECP256K1_CHECKMEM_CHECK(nonce,32);
         CHECK(nonce_function_rfc6979(nonce2, zeros, zeros, zeros, NULL, 0) == 1);
-        VG_CHECK(nonce2,32);
+        SECP256K1_CHECKMEM_CHECK(nonce2,32);
         CHECK(nonce_function_rfc6979(nonce3, zeros, zeros, NULL, (void *)zeros, 0) == 1);
-        VG_CHECK(nonce3,32);
+        SECP256K1_CHECKMEM_CHECK(nonce3,32);
         CHECK(nonce_function_rfc6979(nonce4, zeros, zeros, zeros, (void *)zeros, 0) == 1);
-        VG_CHECK(nonce4,32);
+        SECP256K1_CHECKMEM_CHECK(nonce4,32);
         CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0);
         CHECK(secp256k1_memcmp_var(nonce, nonce3, 32) != 0);
         CHECK(secp256k1_memcmp_var(nonce, nonce4, 32) != 0);