Skip to content

Commit 2033db0

Browse files
committed
Check that VERIFY_CHECK is side-effect free
1 parent 27d0cef commit 2033db0

File tree

3 files changed

+75
-3
lines changed

3 files changed

+75
-3
lines changed

build-aux/m4/bitcoin_secp.m4

+31
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,37 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
99
AC_MSG_RESULT([$has_64bit_asm])
1010
])
1111

12+
AC_DEFUN([SECP_CHECK_SIDE_EFFECT_FREE],[
13+
AC_MSG_CHECKING(whether compiler detects side effect free statements)
14+
AC_LINK_IFELSE([AC_LANG_SOURCE([[
15+
#include <stdlib.h>
16+
static int my_memcmp(const void *s1, const void *s2, size_t n) {
17+
const unsigned char *p1 = s1, *p2 = s2;
18+
size_t i;
19+
for (i = 0; i < n; i++) {
20+
int diff = p1[i] - p2[i];
21+
if (diff != 0) {
22+
return diff;
23+
}
24+
}
25+
return 0;
26+
}
27+
28+
static int side_effect_free(int val, const int* ptr) {
29+
static const unsigned char foo[32] = {1,2,3};
30+
if (!my_memcmp(ptr, foo, val)) return 0;
31+
return (((val + 13) * ptr[0] ^ 7) * ptr[ptr[1]] / 11) * (int)ptr == 0;
32+
}
33+
34+
extern int should_not_survive;
35+
int main(int argc, char** argv) {
36+
(void)(should_not_survive || side_effect_free(argc, (const int*)argv));
37+
return 0;
38+
}
39+
]])],[has_check_side_effect_free=yes],[has_check_side_effect_free=no])
40+
AC_MSG_RESULT([$has_check_side_effect_free])
41+
])
42+
1243
dnl
1344
AC_DEFUN([SECP_OPENSSL_CHECK],[
1445
has_libcrypto=no

configure.ac

+27
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ AC_ARG_ENABLE(external_default_callbacks,
160160
[use_external_default_callbacks=$enableval],
161161
[use_external_default_callbacks=no])
162162

163+
AC_ARG_ENABLE(side_effect_free_check,
164+
AS_HELP_STRING([--enable-side-effect-free-check],[enable checking assertions are side-effect free (yes/no/auto) [default=no]]),
165+
[use_side_effect_free_check=$enableval],
166+
[use_side_effect_free_check=no])
167+
163168
# Test-only override of the (autodetected by the C code) "widemul" setting.
164169
# Legal values are int64 (for [u]int64_t), int128 (for [unsigned] __int128), and auto (the default).
165170
AC_ARG_WITH([test-override-wide-multiply], [] ,[set_widemul=$withval], [set_widemul=auto])
@@ -218,6 +223,18 @@ else
218223
CFLAGS="-O2 $CFLAGS"
219224
fi
220225

226+
set_check_side_effect_free=no
227+
if test x"$use_side_effect_free_check" != x"no"; then
228+
SECP_CHECK_SIDE_EFFECT_FREE
229+
if test x"$has_check_side_effect_free" = x"yes"; then
230+
set_check_side_effect_free=yes
231+
else
232+
if test x"$use_side_effect_free_check" != x"auto"; then
233+
AC_MSG_ERROR([Side effect free checking requested but not compatible with compiler])
234+
fi
235+
fi
236+
fi
237+
221238
if test x"$req_asm" = x"auto"; then
222239
SECP_64BIT_ASM_CHECK
223240
if test x"$has_64bit_asm" = x"yes"; then
@@ -292,6 +309,16 @@ if test x"$use_external_asm" = x"yes"; then
292309
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
293310
fi
294311

312+
case $set_check_side_effect_free in
313+
yes)
314+
AC_DEFINE(CHECK_SIDE_EFFECT_FREE, 1, [Define this symbol to enable checks for side-effect free statements])
315+
;;
316+
no)
317+
;;
318+
*)
319+
AC_MSG_ERROR([invalid selection for check_side_effect_free])
320+
;;
321+
esac
295322

296323
# Select wide multiplication implementation
297324
case $set_widemul in

src/util.h

+17-3
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,29 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback *
5757
} while(0)
5858
#endif
5959

60-
/* Like assert(), but when VERIFY is defined, and side-effect safe. */
60+
#ifdef CHECK_SIDE_EFFECT_FREE
61+
/* When our compiler is capable to optimizing away references to variables
62+
* that control execution of a side-effect free statement, use this to
63+
* check that VERIFY_CHECK conditions are safe.
64+
*
65+
* Credit to: https://stackoverflow.com/a/35294344 */
66+
extern int secp256k1_not_supposed_to_survive;
67+
#define ASSERT_NO_SIDE_EFFECT(cond) do { (void)(secp256k1_not_supposed_to_survive || (cond)); } while(0)
68+
#else
69+
/* If we can't use that, just run it in an unexecuted branch, to make sure it is still
70+
* syntactically valid. */
71+
#define ASSERT_NO_SIDE_EFFECT(cond) do { if (0) { (void)(cond); } } while(0)
72+
#endif
73+
74+
/* VERIFY_CHECK is like assert(), but gated by -DVERIFY, and can be checked for side-effects. */
6175
#if defined(COVERAGE)
6276
#define VERIFY_CHECK(check)
6377
#define VERIFY_SETUP(stmt)
6478
#elif defined(VERIFY)
65-
#define VERIFY_CHECK CHECK
79+
#define VERIFY_CHECK(cond) do { ASSERT_NO_SIDE_EFFECT(cond); CHECK(cond); } while (0)
6680
#define VERIFY_SETUP(stmt) do { stmt; } while(0)
6781
#else
68-
#define VERIFY_CHECK(cond) do { (void)(cond); } while(0)
82+
#define VERIFY_CHECK(cond) do { ASSERT_NO_SIDE_EFFECT(cond); } while (0)
6983
#define VERIFY_SETUP(stmt)
7084
#endif
7185

0 commit comments

Comments
 (0)