Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ config.sub
configure
depcomp
dist
fuzz/fuzz_*
!fuzz/fuzz_*.c
install-sh
libtool
ltmain.sh
Expand Down
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
ACLOCAL_AMFLAGS = -I tools/build-aux/m4
AUTOMAKE_OPTIONS = foreign
SUBDIRS = src

if BUILD_FUZZ
SUBDIRS += fuzz
endif
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ $ brew install swig
- `--disable-tests`. Disables building library tests. (default: no)
- `--disable-clear-tests`. Disables just the test_clear test (required to pass
the test suite with some compilers). (default: no)
- `--enable-fuzzing`. Enables fuzzing support by compiling with
`-fsanitize=fuzzer-no-link` and builds fuzz targets. (default: no).
- `--enable-address-sanitizer`. Enables the address sanitizer for detecting
memory errors. (default: no).
- `--enable-ub-sanitizer`. Enables the undefined behavior sanitizer. (default: no).

### Recommended development configure options

Expand Down
31 changes: 31 additions & 0 deletions _CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@ option(WALLYCORE_ENABLE_TESTS "Build tests" OFF)
option(WALLYCORE_INSTALL "Enable install" OFF)
option(WALLYCORE_COVERAGE "Enable coverage" OFF)
option(WALLYCORE_BUILD_ELEMENTS "Build elements" ON)
option(WALLYCORE_ENABLE_FUZZING "Enable fuzzing support" OFF)
option(WALLYCORE_ENABLE_ADDRESS_SANITIZER "Enable address sanitizer" OFF)
option(WALLYCORE_ENABLE_UB_SANITIZER "Enable undefined behavior sanitizer" OFF)

if(WALLYCORE_ENABLE_FUZZING)
add_compile_options(-fsanitize=fuzzer-no-link)
endif()

if(WALLYCORE_ENABLE_ADDRESS_SANITIZER)
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
endif()

if(WALLYCORE_ENABLE_UB_SANITIZER)
add_compile_options(-fsanitize=undefined)
add_link_options(-fsanitize=undefined)
endif()

include(cmake/utils.cmake)
generate_config_file()
Expand Down Expand Up @@ -42,6 +59,20 @@ add_subdirectory(./src/secp256k1/)

add_subdirectory(./src)

if(WALLYCORE_ENABLE_FUZZING)
add_executable(fuzz_psbt_from_bytes fuzz/fuzz_psbt_from_bytes.c)
target_include_directories(fuzz_psbt_from_bytes PRIVATE include)
target_link_libraries(fuzz_psbt_from_bytes PRIVATE wallycore)
target_link_options(fuzz_psbt_from_bytes PRIVATE -fsanitize=fuzzer)
set_target_properties(fuzz_psbt_from_bytes PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/fuzz)

add_executable(fuzz_tx_from_bytes fuzz/fuzz_tx_from_bytes.c)
target_include_directories(fuzz_tx_from_bytes PRIVATE include)
target_link_libraries(fuzz_tx_from_bytes PRIVATE wallycore)
target_link_options(fuzz_tx_from_bytes PRIVATE -fsanitize=fuzzer)
set_target_properties(fuzz_tx_from_bytes PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/fuzz)
endif()

if(NOT WALLYCORE_ENABLE_TESTS)
return()
endif()
Expand Down
31 changes: 31 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,17 @@ AC_ARG_ENABLE(secp256k1-tests,
AC_ARG_ENABLE(asm,
AS_HELP_STRING([--enable-asm],[enable assembly language implementations (default: yes)]),
[asm=$enableval], [asm=yes])
AC_ARG_ENABLE(fuzzing,
AS_HELP_STRING([--enable-fuzzing],[enable fuzzing support (default: no)]),
[fuzzing=$enableval], [fuzzing=no])
AC_ARG_ENABLE(address-sanitizer,
AS_HELP_STRING([--enable-address-sanitizer],[enable address sanitizer (default: no)]),
[address_sanitizer=$enableval], [address_sanitizer=no])
AC_ARG_ENABLE(ub-sanitizer,
AS_HELP_STRING([--enable-ub-sanitizer],[enable undefined behavior sanitizer (default: no)]),
[ub_sanitizer=$enableval], [ub_sanitizer=no])
AM_CONDITIONAL([RUN_TESTS], [test "x$tests" = "xyes"])
AM_CONDITIONAL([BUILD_FUZZ], [test "x$fuzzing" = "xyes"])
AM_CONDITIONAL([BUILD_ELEMENTS], [test "x$elements" = "xyes"])
AM_CONDITIONAL([WALLY_ABI_NO_ELEMENTS], [test "x$elements_abi" = "xno"])
AM_CONDITIONAL([BUILD_STANDARD_SECP], [test "x$standard_secp" = "xyes"])
Expand Down Expand Up @@ -156,6 +166,23 @@ if test "x$builtin_memset" = "xno"; then
AX_CHECK_COMPILE_FLAG([-fno-builtin-memset], [AM_CFLAGS="$AM_CFLAGS -fno-builtin"])
fi

if test "x$fuzzing" = "xyes"; then
AX_CHECK_COMPILE_FLAG([-fsanitize=fuzzer-no-link], [AM_CFLAGS="$AM_CFLAGS -fsanitize=fuzzer-no-link"])
fi

if test "x$address_sanitizer" = "xyes"; then
AX_CHECK_COMPILE_FLAG([-fsanitize=address], [AM_CFLAGS="$AM_CFLAGS -fsanitize=address"])
AX_CHECK_LINK_FLAG([-fsanitize=address], [LDFLAGS="$LDFLAGS -fsanitize=address"])
fi

if test "x$ub_sanitizer" = "xyes"; then
AX_CHECK_COMPILE_FLAG([-fsanitize=undefined], [AM_CFLAGS="$AM_CFLAGS -fsanitize=undefined"])
AX_CHECK_LINK_FLAG([-fsanitize=undefined], [LDFLAGS="$LDFLAGS -fsanitize=undefined"])
# ubsan complains about unaligned reads/writes even though they are legal
# on x86 archs. Force alignment adjustment to avoid false positives.
AX_CHECK_COMPILE_FLAG([-DAVOID_UNALIGNED_ACCESS=1], [AM_CFLAGS="$AM_CFLAGS -DAVOID_UNALIGNED_ACCESS=1"])
fi

# -flax-vector-conversions is needed for our arm assembly
AX_CHECK_COMPILE_FLAG([-flax-vector-conversions], [AM_CFLAGS="$AM_CFLAGS -flax-vector-conversions"])
AX_CHECK_COMPILE_FLAG([-fno-strict-aliasing], [NOALIAS_CFLAGS="-fno-strict-aliasing"])
Expand Down Expand Up @@ -420,6 +447,10 @@ AC_CONFIG_FILES([
src/wallycore.pc
])

if test "x$fuzzing" = "xyes"; then
AC_CONFIG_FILES([fuzz/Makefile])
fi

secp_asm="--with-asm=auto"
if test "x$asm" = "xno"; then
secp_asm="--with-asm=no"
Expand Down
11 changes: 11 additions & 0 deletions fuzz/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
noinst_PROGRAMS = fuzz_psbt_from_bytes fuzz_tx_from_bytes

fuzz_psbt_from_bytes_SOURCES = fuzz_psbt_from_bytes.c
fuzz_psbt_from_bytes_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS)
fuzz_psbt_from_bytes_LDFLAGS = -fsanitize=fuzzer
fuzz_psbt_from_bytes_LDADD = $(top_builddir)/src/libwallycore.la

fuzz_tx_from_bytes_SOURCES = fuzz_tx_from_bytes.c
fuzz_tx_from_bytes_CFLAGS = -I$(top_srcdir)/include $(AM_CFLAGS)
fuzz_tx_from_bytes_LDFLAGS = -fsanitize=fuzzer
fuzz_tx_from_bytes_LDADD = $(top_builddir)/src/libwallycore.la
36 changes: 36 additions & 0 deletions fuzz/fuzz_psbt_from_bytes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <wally_psbt.h>

static void test_fuzz_psbt_from_bytes(const uint8_t *data, size_t size, uint32_t flags)
{
struct wally_psbt *psbt = NULL;
int ret;

ret = wally_psbt_from_bytes(data, size, flags, &psbt);
if (psbt) {
if (ret == WALLY_OK && flags == WALLY_PSBT_PARSE_FLAG_STRICT) {
/* Parsing succeeded: try to serialize it back to bytes */
size_t len = 0, written = 0;
ret = wally_psbt_get_length(psbt, 0, &len);
if (ret == WALLY_OK && len) {
unsigned char *bytes = malloc(len);
if (bytes) {
wally_psbt_to_bytes(psbt, 0, bytes, len, &written);
free(bytes);
}
}
}
wally_psbt_free(psbt);
}
}

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
/* Test strict parsing */
test_fuzz_psbt_from_bytes(data, size, WALLY_PSBT_PARSE_FLAG_STRICT);
/* Test loose parsing */
test_fuzz_psbt_from_bytes(data, size, WALLY_PSBT_PARSE_FLAG_LOOSE);
/* Test default flags (no flags) */
test_fuzz_psbt_from_bytes(data, size, 0);

return 0;
}
43 changes: 43 additions & 0 deletions fuzz/fuzz_tx_from_bytes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <wally_transaction.h>

static void test_tx_from_bytes(const uint8_t *data, size_t size, uint32_t flags)
{
struct wally_tx *tx = NULL;
int ret;

ret = wally_tx_from_bytes(data, size, flags, &tx);
if (tx) {
if (ret == WALLY_OK &&
(flags == WALLY_TX_FLAG_USE_WITNESS ||
flags == (WALLY_TX_FLAG_USE_WITNESS|WALLY_TX_FLAG_USE_ELEMENTS))) {
/* Parsing succeeded: try to serialize it back to bytes */
size_t len = 0, written = 0;
ret = wally_tx_get_length(tx, flags, &len);
if (ret == WALLY_OK && len) {
unsigned char *bytes = malloc(len);
if (bytes) {
wally_tx_to_bytes(tx, flags, bytes, len, &written);
free(bytes);
}
}
}
wally_tx_free(tx);
}
}

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
static const uint32_t flags[6] = {
0,
WALLY_TX_FLAG_USE_WITNESS,
WALLY_TX_FLAG_USE_ELEMENTS,
WALLY_TX_FLAG_USE_WITNESS | WALLY_TX_FLAG_USE_ELEMENTS,
WALLY_TX_FLAG_ALLOW_PARTIAL,
WALLY_TX_FLAG_PRE_BIP144
};

for (size_t i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
test_tx_from_bytes(data, size, flags[i]);

return 0;
}
13 changes: 8 additions & 5 deletions src/ccan_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@
#define HAVE_BSWAP_64 0
#endif

#if defined(HAVE_UNALIGNED_ACCESS) && defined(__arm__)
/* arm unaligned access is incomplete, in that e.g. byte swap instructions
* can fault on unaligned addresses where a normal load/store would be fine.
* Since the compiler can optimise some of our accesses into operations like
* byte swaps, treat this platform as though it doesn't have unaligned access.
#if defined(AVOID_UNALIGNED_ACCESS) || (defined(HAVE_UNALIGNED_ACCESS) && defined(__arm__))
/* Disable unaligned access when:
* 1) Explicitly asked to via AVOID_UNALIGNED_ACCESS (e.g. for ubsan builds)
* 2) For arm builds where unaligned access is incomplete, in that e.g. byte
* swap instructions can fault on unaligned addresses where a normal load/store
* would be fine. Since the compiler can optimise some of our accesses into
* operations like byte swaps, treat this platform as though it doesn't have
* unaligned access.
*/
#undef HAVE_UNALIGNED_ACCESS
#define HAVE_UNALIGNED_ACCESS 0
Expand Down
40 changes: 26 additions & 14 deletions src/psbt.c
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,15 @@ static int psbt_input_free(struct wally_psbt_input *input, bool free_parent)
return WALLY_OK;
}

static void psbt_inputs_free(struct wally_psbt_input *inputs, size_t num_inputs)
{
if (inputs) {
for (size_t i = 0; i < num_inputs; ++i)
psbt_input_free(&inputs[i], false);
wally_free(inputs);
}
}

MAP_INNER_FIELD(output, redeem_script, PSBT_OUT_REDEEM_SCRIPT, psbt_fields)
MAP_INNER_FIELD(output, witness_script, PSBT_OUT_WITNESS_SCRIPT, psbt_fields)
MAP_INNER_FIELD(output, taproot_internal_key, PSBT_OUT_TAP_INTERNAL_KEY, psbt_fields)
Expand Down Expand Up @@ -1149,6 +1158,15 @@ static int psbt_output_free(struct wally_psbt_output *output, bool free_parent)
return WALLY_OK;
}

static void psbt_outputs_free(struct wally_psbt_output *outputs, size_t num_outputs)
{
if (outputs) {
for (size_t i = 0; i < num_outputs; ++i)
psbt_output_free(&outputs[i], false);
wally_free(outputs);
}
}

static int psbt_init(uint32_t version, size_t num_inputs, size_t num_outputs,
size_t num_unknowns, uint32_t flags,
size_t max_num_inputs, size_t max_num_outputs,
Expand Down Expand Up @@ -1291,17 +1309,11 @@ static void psbt_claim_allocated_inputs(struct wally_psbt *psbt, size_t num_inpu

int wally_psbt_free(struct wally_psbt *psbt)
{
size_t i;
if (psbt) {
wally_tx_free(psbt->tx);
for (i = 0; i < psbt->num_inputs; ++i)
psbt_input_free(&psbt->inputs[i], false);
psbt_inputs_free(psbt->inputs, psbt->num_inputs);
psbt_outputs_free(psbt->outputs, psbt->num_outputs);

wally_free(psbt->inputs);
for (i = 0; i < psbt->num_outputs; ++i)
psbt_output_free(&psbt->outputs[i], false);

wally_free(psbt->outputs);
wally_map_clear(&psbt->unknowns);
wally_map_clear(&psbt->global_xpubs);
#ifdef BUILD_ELEMENTS
Expand Down Expand Up @@ -1591,31 +1603,31 @@ static int psbt_set_global_tx(struct wally_psbt *psbt, struct wally_tx *tx, bool

if (psbt->inputs_allocation_len < tx->num_inputs) {
new_inputs = wally_malloc(tx->num_inputs * sizeof(struct wally_psbt_input));
for (i = 0; i < tx->num_inputs; ++i)
for (i = 0; new_inputs && i < tx->num_inputs; ++i)
psbt_input_init(&new_inputs[i]);
}

if (psbt->outputs_allocation_len < tx->num_outputs) {
new_outputs = wally_malloc(tx->num_outputs * sizeof(struct wally_psbt_output));
for (i = 0; i < tx->num_outputs; ++i)
for (i = 0; new_outputs && i < tx->num_outputs; ++i)
psbt_output_init(&new_outputs[i]);
}

if ((psbt->inputs_allocation_len < tx->num_inputs && !new_inputs) ||
(psbt->outputs_allocation_len < tx->num_outputs && !new_outputs)) {
wally_free(new_inputs);
wally_free(new_outputs);
psbt_inputs_free(new_inputs, tx->num_inputs);
psbt_outputs_free(new_outputs, tx->num_outputs);
wally_tx_free(new_tx);
return WALLY_ENOMEM;
}

if (new_inputs) {
wally_free(psbt->inputs);
psbt_inputs_free(psbt->inputs, psbt->num_inputs);
psbt->inputs = new_inputs;
psbt->inputs_allocation_len = tx->num_inputs;
}
if (new_outputs) {
wally_free(psbt->outputs);
psbt_outputs_free(psbt->outputs, psbt->num_outputs);
psbt->outputs = new_outputs;
psbt->outputs_allocation_len = tx->num_outputs;
}
Expand Down
Loading
Loading