From 9ca3279507425edaefc30cf1183522a627be4e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 5 Apr 2025 15:53:19 +0200 Subject: [PATCH] test: add tests for zero sized realloc(3) C23 declared calling realloc(3) with a non-NULL pointer and zero size Undefined behavior. Check that hardened_malloc handles that case sanely by free'ing the old pointer and returning a special pointer, like `malloc(3)` called with size zero. --- test/.gitignore | 3 +++ test/Makefile | 5 ++++- test/realloc_c23_undefined_behaviour.c | 19 +++++++++++++++++ ...lloc_c23_undefined_behaviour_double_free.c | 19 +++++++++++++++++ ...c_c23_undefined_behaviour_use_after_free.c | 21 +++++++++++++++++++ test/test_smc.py | 14 +++++++++++++ 6 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 test/realloc_c23_undefined_behaviour.c create mode 100644 test/realloc_c23_undefined_behaviour_double_free.c create mode 100644 test/realloc_c23_undefined_behaviour_use_after_free.c diff --git a/test/.gitignore b/test/.gitignore index d37a6a7..e11cab7 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -41,4 +41,7 @@ overflow_small_8_byte uninitialized_read_large uninitialized_read_small realloc_init +realloc_c23_undefined_behaviour +realloc_c23_undefined_behaviour_double_free +realloc_c23_undefined_behaviour_use_after_free __pycache__/ diff --git a/test/Makefile b/test/Makefile index 0eb3921..5462950 100644 --- a/test/Makefile +++ b/test/Makefile @@ -67,7 +67,10 @@ EXECUTABLES := \ invalid_malloc_object_size_small \ invalid_malloc_object_size_small_quarantine \ impossibly_large_malloc \ - realloc_init + realloc_init \ + realloc_c23_undefined_behaviour \ + realloc_c23_undefined_behaviour_double_free \ + realloc_c23_undefined_behaviour_use_after_free all: $(EXECUTABLES) diff --git a/test/realloc_c23_undefined_behaviour.c b/test/realloc_c23_undefined_behaviour.c new file mode 100644 index 0000000..76d5a6e --- /dev/null +++ b/test/realloc_c23_undefined_behaviour.c @@ -0,0 +1,19 @@ +#include +#include + +#include "test_util.h" + +OPTNONE int main(void) { + char *p, *q, *r; + + p = malloc(16); + if (!p) { + return 1; + } + + q = realloc(p, 0); + + free(q); + + return 0; +} diff --git a/test/realloc_c23_undefined_behaviour_double_free.c b/test/realloc_c23_undefined_behaviour_double_free.c new file mode 100644 index 0000000..984df9f --- /dev/null +++ b/test/realloc_c23_undefined_behaviour_double_free.c @@ -0,0 +1,19 @@ +#include +#include + +#include "test_util.h" + +OPTNONE int main(void) { + char *p, *q, *r; + + p = malloc(16); + if (!p) { + return 1; + } + + q = realloc(p, 0); + + free(p); + + return 0; +} diff --git a/test/realloc_c23_undefined_behaviour_use_after_free.c b/test/realloc_c23_undefined_behaviour_use_after_free.c new file mode 100644 index 0000000..9f478da --- /dev/null +++ b/test/realloc_c23_undefined_behaviour_use_after_free.c @@ -0,0 +1,21 @@ +#include +#include + +#include "test_util.h" + +OPTNONE int main(void) { + char *p, *q, *r; + + p = malloc(256 * 1024); + if (!p) { + return 1; + } + + q = realloc(p, 0); + + printf("%c\n", *p); + + free(q); + + return 0; +} diff --git a/test/test_smc.py b/test/test_smc.py index 170278e..1173cda 100644 --- a/test/test_smc.py +++ b/test/test_smc.py @@ -169,6 +169,20 @@ class TestSimpleMemoryCorruption(unittest.TestCase): self.assertEqual(stderr.decode("utf-8"), "fatal allocator error: invalid realloc\n") + def test_realloc_c23_undefined_behaviour(self): + _stdout, stderr, returncode = self.run_test("realloc_c23_undefined_behaviour") + self.assertEqual(returncode, 0) + + def test_realloc_c23_undefined_behaviour_double_free(self): + _stdout, stderr, returncode = self.run_test("realloc_c23_undefined_behaviour_double_free") + self.assertEqual(returncode, -6) + self.assertEqual(stderr.decode("utf-8"), + "fatal allocator error: double free (quarantine)\n") + + def test_realloc_c23_undefined_behaviour_use_after_free(self): + _stdout, stderr, returncode = self.run_test("realloc_c23_undefined_behaviour_use_after_free") + self.assertEqual(returncode, -11) + def test_write_after_free_large_reuse(self): _stdout, _stderr, returncode = self.run_test( "write_after_free_large_reuse")