[libbacktrace] Add allocfail.sh test-case Add test-case that forces alloc.c functions to fail, and check whether fail handling is robust. This is the test-case for "[libbacktrace] Fix segfault upon allocation failure". Without that patch, this test-case fails like this: ... allocfail.sh: line 71: 26041 Segmentation fault (core dumped) \ ./allocfail $i > /dev/null 2>&1 Unallowed fail found: 13 FAIL allocfail.sh (exit status: 1) ... This is a seperate patch because the test-case is nontrivial. Bootstrapped and reg-tested on x86_64. 2018-11-27 Tom de Vries * Makefile.am (TESTS): Add allocfail.sh. (check_PROGRAMS): Add allocfail. * Makefile.in: Regenerate. * instrumented_alloc.c: New file. Redefine malloc and realloc. Include alloc.c. * allocfail.c: New file. * allocfail.sh: New file. --- libbacktrace/Makefile.am | 20 ++++++ libbacktrace/Makefile.in | 81 ++++++++++++++++++----- libbacktrace/allocfail.c | 136 ++++++++++++++++++++++++++++++++++++++ libbacktrace/allocfail.sh | 105 +++++++++++++++++++++++++++++ libbacktrace/instrumented_alloc.c | 114 ++++++++++++++++++++++++++++++++ 5 files changed, 439 insertions(+), 17 deletions(-) diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am index 1a3680bc98c..9d489f7b418 100644 --- a/libbacktrace/Makefile.am +++ b/libbacktrace/Makefile.am @@ -145,6 +145,26 @@ unittest_alloc_LDADD = libbacktrace_alloc.la check_PROGRAMS += unittest_alloc +check_LTLIBRARIES += libbacktrace_instrumented_alloc.la + +libbacktrace_instrumented_alloc_la_SOURCES = $(libbacktrace_la_SOURCES) +libbacktrace_instrumented_alloc_la_LIBADD = $(BACKTRACE_FILE) $(FORMAT_FILE) \ + read.lo instrumented_alloc.lo + +libbacktrace_instrumented_alloc_la_DEPENDENCIES = \ + $(libbacktrace_instrumented_alloc_la_LIBADD) + +instrumented_alloc.lo: alloc.c + +allocfail_SOURCES = allocfail.c testlib.c +allocfail_LDADD = libbacktrace_instrumented_alloc.la + +check_PROGRAMS += allocfail + +allocfail.sh: allocfail + +TESTS += allocfail.sh + btest_SOURCES = btest.c testlib.c btest_CFLAGS = $(AM_CFLAGS) -g -O btest_LDADD = libbacktrace.la diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in index 6eaa1e28c01..7b18b9834e0 100644 --- a/libbacktrace/Makefile.in +++ b/libbacktrace/Makefile.in @@ -123,13 +123,14 @@ target_triplet = @target@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) @NATIVE_TRUE@am__append_1 = test_elf test_xcoff_32 test_xcoff_64 \ @NATIVE_TRUE@ test_pecoff test_unknown unittest unittest_alloc \ -@NATIVE_TRUE@ btest btest_alloc stest stest_alloc ztest \ -@NATIVE_TRUE@ ztest_alloc edtest edtest_alloc -@HAVE_ZLIB_TRUE@@NATIVE_TRUE@am__append_2 = -lz +@NATIVE_TRUE@ allocfail btest btest_alloc stest stest_alloc \ +@NATIVE_TRUE@ ztest ztest_alloc edtest edtest_alloc +@NATIVE_TRUE@am__append_2 = allocfail.sh @HAVE_ZLIB_TRUE@@NATIVE_TRUE@am__append_3 = -lz -@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_4 = ttest ttest_alloc -@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_5 = dtest -@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am__append_6 = ctestg ctesta \ +@HAVE_ZLIB_TRUE@@NATIVE_TRUE@am__append_4 = -lz +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_5 = ttest ttest_alloc +@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_6 = dtest +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am__append_7 = ctestg ctesta \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctestg_alloc \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctesta_alloc subdir = . @@ -169,6 +170,11 @@ am__objects_1 = atomic.lo dwarf.lo fileline.lo posix.lo print.lo \ @NATIVE_TRUE@am_libbacktrace_alloc_la_OBJECTS = $(am__objects_1) libbacktrace_alloc_la_OBJECTS = $(am_libbacktrace_alloc_la_OBJECTS) @NATIVE_TRUE@am_libbacktrace_alloc_la_rpath = +@NATIVE_TRUE@am_libbacktrace_instrumented_alloc_la_OBJECTS = \ +@NATIVE_TRUE@ $(am__objects_1) +libbacktrace_instrumented_alloc_la_OBJECTS = \ + $(am_libbacktrace_instrumented_alloc_la_OBJECTS) +@NATIVE_TRUE@am_libbacktrace_instrumented_alloc_la_rpath = @NATIVE_TRUE@am_libbacktrace_noformat_la_OBJECTS = $(am__objects_1) libbacktrace_noformat_la_OBJECTS = \ $(am_libbacktrace_noformat_la_OBJECTS) @@ -176,11 +182,11 @@ libbacktrace_noformat_la_OBJECTS = \ @NATIVE_TRUE@am__EXEEXT_1 = test_elf$(EXEEXT) test_xcoff_32$(EXEEXT) \ @NATIVE_TRUE@ test_xcoff_64$(EXEEXT) test_pecoff$(EXEEXT) \ @NATIVE_TRUE@ test_unknown$(EXEEXT) unittest$(EXEEXT) \ -@NATIVE_TRUE@ unittest_alloc$(EXEEXT) btest$(EXEEXT) \ -@NATIVE_TRUE@ btest_alloc$(EXEEXT) stest$(EXEEXT) \ -@NATIVE_TRUE@ stest_alloc$(EXEEXT) ztest$(EXEEXT) \ -@NATIVE_TRUE@ ztest_alloc$(EXEEXT) edtest$(EXEEXT) \ -@NATIVE_TRUE@ edtest_alloc$(EXEEXT) +@NATIVE_TRUE@ unittest_alloc$(EXEEXT) allocfail$(EXEEXT) \ +@NATIVE_TRUE@ btest$(EXEEXT) btest_alloc$(EXEEXT) \ +@NATIVE_TRUE@ stest$(EXEEXT) stest_alloc$(EXEEXT) \ +@NATIVE_TRUE@ ztest$(EXEEXT) ztest_alloc$(EXEEXT) \ +@NATIVE_TRUE@ edtest$(EXEEXT) edtest_alloc$(EXEEXT) @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__EXEEXT_2 = ttest$(EXEEXT) \ @HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ ttest_alloc$(EXEEXT) @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am__EXEEXT_3 = \ @@ -188,6 +194,11 @@ libbacktrace_noformat_la_OBJECTS = \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctesta$(EXEEXT) \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctestg_alloc$(EXEEXT) \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctesta_alloc$(EXEEXT) +@NATIVE_TRUE@am_allocfail_OBJECTS = allocfail.$(OBJEXT) \ +@NATIVE_TRUE@ testlib.$(OBJEXT) +allocfail_OBJECTS = $(am_allocfail_OBJECTS) +@NATIVE_TRUE@allocfail_DEPENDENCIES = \ +@NATIVE_TRUE@ libbacktrace_instrumented_alloc.la @NATIVE_TRUE@am_btest_OBJECTS = btest-btest.$(OBJEXT) \ @NATIVE_TRUE@ btest-testlib.$(OBJEXT) btest_OBJECTS = $(am_btest_OBJECTS) @@ -356,8 +367,9 @@ am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \ $(libbacktrace_alloc_la_SOURCES) \ - $(libbacktrace_noformat_la_SOURCES) $(btest_SOURCES) \ - $(btest_alloc_SOURCES) $(ctesta_SOURCES) \ + $(libbacktrace_instrumented_alloc_la_SOURCES) \ + $(libbacktrace_noformat_la_SOURCES) $(allocfail_SOURCES) \ + $(btest_SOURCES) $(btest_alloc_SOURCES) $(ctesta_SOURCES) \ $(ctesta_alloc_SOURCES) $(ctestg_SOURCES) \ $(ctestg_alloc_SOURCES) $(edtest_SOURCES) \ $(edtest_alloc_SOURCES) $(stest_SOURCES) \ @@ -774,9 +786,10 @@ libbacktrace_la_LIBADD = \ $(ALLOC_FILE) libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) -TESTS = $(check_PROGRAMS) $(am__append_5) +TESTS = $(check_PROGRAMS) $(am__append_2) $(am__append_6) @NATIVE_TRUE@check_LTLIBRARIES = libbacktrace_alloc.la \ -@NATIVE_TRUE@ libbacktrace_noformat.la +@NATIVE_TRUE@ libbacktrace_noformat.la \ +@NATIVE_TRUE@ libbacktrace_instrumented_alloc.la @NATIVE_TRUE@libbacktrace_alloc_la_SOURCES = $(libbacktrace_la_SOURCES) @NATIVE_TRUE@libbacktrace_alloc_la_LIBADD = $(BACKTRACE_FILE) $(FORMAT_FILE) read.lo alloc.lo @NATIVE_TRUE@libbacktrace_alloc_la_DEPENDENCIES = $(libbacktrace_alloc_la_LIBADD) @@ -797,6 +810,15 @@ TESTS = $(check_PROGRAMS) $(am__append_5) @NATIVE_TRUE@unittest_LDADD = libbacktrace.la @NATIVE_TRUE@unittest_alloc_SOURCES = $(unittest_SOURCES) @NATIVE_TRUE@unittest_alloc_LDADD = libbacktrace_alloc.la +@NATIVE_TRUE@libbacktrace_instrumented_alloc_la_SOURCES = $(libbacktrace_la_SOURCES) +@NATIVE_TRUE@libbacktrace_instrumented_alloc_la_LIBADD = $(BACKTRACE_FILE) $(FORMAT_FILE) \ +@NATIVE_TRUE@ read.lo instrumented_alloc.lo + +@NATIVE_TRUE@libbacktrace_instrumented_alloc_la_DEPENDENCIES = \ +@NATIVE_TRUE@ $(libbacktrace_instrumented_alloc_la_LIBADD) + +@NATIVE_TRUE@allocfail_SOURCES = allocfail.c testlib.c +@NATIVE_TRUE@allocfail_LDADD = libbacktrace_instrumented_alloc.la @NATIVE_TRUE@btest_SOURCES = btest.c testlib.c @NATIVE_TRUE@btest_CFLAGS = $(AM_CFLAGS) -g -O @NATIVE_TRUE@btest_LDADD = libbacktrace.la @@ -809,9 +831,9 @@ TESTS = $(check_PROGRAMS) $(am__append_5) @NATIVE_TRUE@stest_alloc_LDADD = libbacktrace_alloc.la @NATIVE_TRUE@ztest_SOURCES = ztest.c testlib.c @NATIVE_TRUE@ztest_CFLAGS = -DSRCDIR=\"$(srcdir)\" -@NATIVE_TRUE@ztest_LDADD = libbacktrace.la $(am__append_2) \ +@NATIVE_TRUE@ztest_LDADD = libbacktrace.la $(am__append_3) \ @NATIVE_TRUE@ $(CLOCK_GETTIME_LINK) -@NATIVE_TRUE@ztest_alloc_LDADD = libbacktrace_alloc.la $(am__append_3) \ +@NATIVE_TRUE@ztest_alloc_LDADD = libbacktrace_alloc.la $(am__append_4) \ @NATIVE_TRUE@ $(CLOCK_GETTIME_LINK) @NATIVE_TRUE@ztest_alloc_SOURCES = $(ztest_SOURCES) @NATIVE_TRUE@ztest_alloc_CFLAGS = $(ztest_CFLAGS) @@ -945,6 +967,9 @@ libbacktrace.la: $(libbacktrace_la_OBJECTS) $(libbacktrace_la_DEPENDENCIES) $(EX libbacktrace_alloc.la: $(libbacktrace_alloc_la_OBJECTS) $(libbacktrace_alloc_la_DEPENDENCIES) $(EXTRA_libbacktrace_alloc_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(am_libbacktrace_alloc_la_rpath) $(libbacktrace_alloc_la_OBJECTS) $(libbacktrace_alloc_la_LIBADD) $(LIBS) +libbacktrace_instrumented_alloc.la: $(libbacktrace_instrumented_alloc_la_OBJECTS) $(libbacktrace_instrumented_alloc_la_DEPENDENCIES) $(EXTRA_libbacktrace_instrumented_alloc_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(am_libbacktrace_instrumented_alloc_la_rpath) $(libbacktrace_instrumented_alloc_la_OBJECTS) $(libbacktrace_instrumented_alloc_la_LIBADD) $(LIBS) + libbacktrace_noformat.la: $(libbacktrace_noformat_la_OBJECTS) $(libbacktrace_noformat_la_DEPENDENCIES) $(EXTRA_libbacktrace_noformat_la_DEPENDENCIES) $(AM_V_CCLD)$(LINK) $(am_libbacktrace_noformat_la_rpath) $(libbacktrace_noformat_la_OBJECTS) $(libbacktrace_noformat_la_LIBADD) $(LIBS) @@ -957,6 +982,10 @@ clean-checkPROGRAMS: echo " rm -f" $$list; \ rm -f $$list +allocfail$(EXEEXT): $(allocfail_OBJECTS) $(allocfail_DEPENDENCIES) $(EXTRA_allocfail_DEPENDENCIES) + @rm -f allocfail$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(allocfail_OBJECTS) $(allocfail_LDADD) $(LIBS) + btest$(EXEEXT): $(btest_OBJECTS) $(btest_DEPENDENCIES) $(EXTRA_btest_DEPENDENCIES) @rm -f btest$(EXEEXT) $(AM_V_CCLD)$(btest_LINK) $(btest_OBJECTS) $(btest_LDADD) $(LIBS) @@ -1434,6 +1463,13 @@ unittest_alloc.log: unittest_alloc$(EXEEXT) --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +allocfail.log: allocfail$(EXEEXT) + @p='allocfail$(EXEEXT)'; \ + b='allocfail'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) btest.log: btest$(EXEEXT) @p='btest$(EXEEXT)'; \ b='btest'; \ @@ -1532,6 +1568,13 @@ ctesta_alloc.log: ctesta_alloc$(EXEEXT) --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +allocfail.sh.log: allocfail.sh + @p='allocfail.sh'; \ + b='allocfail.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) dtest.log: dtest @p='dtest'; \ b='dtest'; \ @@ -1695,6 +1738,10 @@ uninstall-am: @NATIVE_TRUE@ $(srcdir)/xcoff.c \ @NATIVE_TRUE@ > $@ +@NATIVE_TRUE@instrumented_alloc.lo: alloc.c + +@NATIVE_TRUE@allocfail.sh: allocfail + @NATIVE_TRUE@edtest2_build.c: gen_edtest2_build; @true @NATIVE_TRUE@gen_edtest2_build: $(srcdir)/edtest2.c @NATIVE_TRUE@ cat $(srcdir)/edtest2.c > tmp-edtest2_build.c diff --git a/libbacktrace/allocfail.c b/libbacktrace/allocfail.c new file mode 100644 index 00000000000..e9149ab977d --- /dev/null +++ b/libbacktrace/allocfail.c @@ -0,0 +1,136 @@ +/* allocfail.c -- Test for libbacktrace library + Copyright (C) 2018 Free Software Foundation, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + (1) Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + (2) Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. */ + +#include +#include +#include +#include +#include + +#include "filenames.h" + +#include "backtrace.h" +#include "backtrace-supported.h" + +#include "testlib.h" + +extern uint64_t get_nr_allocs (void); +extern void set_fail_at_alloc (uint64_t); +extern int at_fail_alloc_p (void); + +static int test1 (void) __attribute__ ((noinline, unused)); +static int f2 (int) __attribute__ ((noinline)); +static int f3 (int, int) __attribute__ ((noinline)); + +static unsigned callback_errors = 0; + +static void +error_callback_full (void *vdata ATTRIBUTE_UNUSED, + const char *msg ATTRIBUTE_UNUSED, + int errnum ATTRIBUTE_UNUSED) +{ + if (at_fail_alloc_p ()) + { + set_fail_at_alloc (0); + return; + } + + callback_errors++; +} + +static int +callback_full (void *vdata ATTRIBUTE_UNUSED, uintptr_t pc ATTRIBUTE_UNUSED, + const char *filename ATTRIBUTE_UNUSED, + int lineno ATTRIBUTE_UNUSED, + const char *function ATTRIBUTE_UNUSED) +{ + + return 0; +} + +static int +test1 (void) +{ + return f2 (__LINE__) + 1; +} + +static int +f2 (int f1line) +{ + return f3 (f1line, __LINE__) + 2; +} + +static int +f3 (int f1line ATTRIBUTE_UNUSED, int f2line ATTRIBUTE_UNUSED) +{ + int i; + + i = backtrace_full (state, 0, callback_full, error_callback_full, NULL); + + if (i != 0) + { + fprintf (stderr, "test1: unexpected return value %d\n", i); + ++failures; + } + + if (callback_errors) + ++failures; + + return failures; +} + +/* Run all the tests. */ + +int +main (int argc, char **argv) +{ + uint64_t fail_at = 0; + + if (argc == 2) + { + fail_at = atoi (argv[1]); + set_fail_at_alloc (fail_at); + } + + state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, + error_callback_full, NULL); + if (state == NULL) + exit (failures ? EXIT_FAILURE : EXIT_SUCCESS); + +#if BACKTRACE_SUPPORTED + test1 (); +#endif + + if (argc == 1) + fprintf (stderr, "%lu\n", get_nr_allocs ()); + + exit (failures ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/libbacktrace/allocfail.sh b/libbacktrace/allocfail.sh new file mode 100755 index 00000000000..91bc7a3e73d --- /dev/null +++ b/libbacktrace/allocfail.sh @@ -0,0 +1,105 @@ +#!/bin/sh + +# allocfail.sh -- Test for libbacktrace library. +# Copyright (C) 2018 Free Software Foundation, Inc. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: + +# (1) Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. + +# (2) Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. + +# (3) The name of the author may not be used to +# endorse or promote products derived from this software without +# specific prior written permission. + +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e +set -o pipefail + +if [ ! -f ./allocfail ]; then + # Hard failure. + exit 99 +fi + +allocs=$(./allocfail 2>&1) +if [ "$allocs" = "" ]; then + # Hard failure. + exit 99 +fi + +# This generates the following output: +# ... +# $ allocfail.sh +# allocs: 80495 +# Status changed to 0 at 1 +# Status changed to 1 at 3 +# Status changed to 0 at 11 +# Status changed to 1 at 12 +# Status changed to 0 at 845 +# ... +# +# We have status 0 for an allocation failure at: +# - 1 because backtrace_create_state handles failure robustly +# - 2 because the fail switches backtrace_full to !can_alloc mode. +# - 11 because failure of elf_open_debugfile_by_buildid does not generate an +# error callback beyond the one for the allocation failure itself. + +echo "allocs: $allocs" + +step=1 +i=1 +passes=0 +prev_status=-1 +while [ $i -le $allocs ]; do + if ./allocfail $i >/dev/null 2>&1; status=$?; then + true + fi + if [ $status -gt 1 ]; then + echo "Unallowed fail found: $i" + # Failure. + exit 1 + fi + + # The test-case would run too long if we would excercise all allocs. + # So, run with step 1 initially, and increase the step once we have 10 + # subsequent passes, and drop back to step 1 once we encounter another + # failure. This takes ~2.6 seconds on an i7-6600U CPU @ 2.60GHz. + if [ $status -eq 0 ]; then + passes=$(($passes + 1)) + if [ $passes -ge 10 ]; then + step=$((step * 10)) + passes=0 + fi + elif [ $status -eq 1 ]; then + passes=0 + step=1 + fi + + if [ $status -ne $prev_status ]; then + echo "Status changed to $status at $i" + fi + prev_status=$status + + i=$(($i + $step)) +done + +# Success. +exit 0 diff --git a/libbacktrace/instrumented_alloc.c b/libbacktrace/instrumented_alloc.c new file mode 100644 index 00000000000..ba42ea65a04 --- /dev/null +++ b/libbacktrace/instrumented_alloc.c @@ -0,0 +1,114 @@ +/* instrumented_alloc.c -- Memory allocation instrumented to fail when + requested, for testing purposes. + Copyright (C) 2018 Free Software Foundation, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + (1) Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + (2) Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. */ + +/* Include all the header files of alloc here, to make sure they're not + processed when including alloc.c below, such that the redefinitions of malloc + and realloc are only effective in alloc.c itself. This does not work for + config.h, because it's not wrapped in "#ifndef CONFIG_H\n#define CONFIG_H" + and "#endif" but that does not seem to be harmful. */ + +#include "config.h" + +#include +#include +#include +#include + +#include "backtrace.h" +#include "internal.h" + +extern void *instrumented_malloc (size_t size); +extern void *instrumented_realloc (void *ptr, size_t size); + +#define malloc instrumented_malloc +#define realloc instrumented_realloc +#include "alloc.c" +#undef malloc +#undef realloc + +static uint64_t nr_allocs = 0; +static uint64_t fail_at_alloc = 0; + +extern int at_fail_alloc_p (void); +extern uint64_t get_nr_allocs (void); +extern void set_fail_at_alloc (uint64_t); + +void * +instrumented_malloc (size_t size) +{ + void *res; + + if (at_fail_alloc_p ()) + return NULL; + + res = malloc (size); + if (res != NULL) + nr_allocs++; + + return res; +} + +void * +instrumented_realloc (void *ptr, size_t size) +{ + void *res; + + if (size != 0) + { + if (at_fail_alloc_p ()) + return NULL; + } + + res = realloc (ptr, size); + if (res != NULL) + nr_allocs++; + + return res; +} + +int +at_fail_alloc_p (void) +{ + return fail_at_alloc == nr_allocs + 1; +} + +uint64_t +get_nr_allocs (void) +{ + return nr_allocs; +} + +void +set_fail_at_alloc (uint64_t nr) +{ + fail_at_alloc = nr; +}