From 716da07dd4a06aac0a0096d4c7066c10a663b0bd 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 9d9c4cfb..cb6495f5 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -44,4 +44,7 @@ uninitialized_read_small realloc_init malloc_zero_different malloc_noreuse +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 41b48a42..2132dcb1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -75,7 +75,10 @@ EXECUTABLES := \ impossibly_large_malloc \ realloc_init \ malloc_zero_different \ - malloc_noreuse + malloc_noreuse \ + 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 00000000..76d5a6eb --- /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 00000000..984df9f9 --- /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 00000000..9f478da9 --- /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 0d5665b1..32676852 100644 --- a/test/test_smc.py +++ b/test/test_smc.py @@ -198,6 +198,20 @@ def test_uninitialized_realloc(self): 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")