The new __free() handlers have no user in the tree yet, so add a selftest that exercises them: it checks that leaving the scope really runs the cleanup, that NULL and error pointers are never passed to free(), and that no_free_ptr() inhibits the cleanup.
Assisted-by: Claude:opus-5-1m Signed-off-by: Ahmad Fatoum <[email protected]> --- test/self/Kconfig | 10 +++++ test/self/Makefile | 1 + test/self/cleanup.c | 89 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 test/self/cleanup.c diff --git a/test/self/Kconfig b/test/self/Kconfig index 85a3ef790116..804fe6ac8d31 100644 --- a/test/self/Kconfig +++ b/test/self/Kconfig @@ -32,6 +32,7 @@ config SELFTEST_ENABLE_ALL select SELFTEST_RANGE select SELFTEST_PRINTF select SELFTEST_MALLOC + select SELFTEST_CLEANUP select SELFTEST_PROGRESS_NOTIFIER select SELFTEST_OF_MANIPULATION select SELFTEST_ENVIRONMENT_VARIABLES if ENVIRONMENT_VARIABLES @@ -71,6 +72,15 @@ config SELFTEST_MALLOC help Tests barebox memory allocator +config SELFTEST_CLEANUP + bool "scope-based cleanup selftest" + help + Tests the __free() cleanup handlers the barebox allocators define, + i.e. that leaving a scope frees the buffer, that NULL and error + pointers are never freed and that no_free_ptr() inhibits cleanup. + + If unsure, say n. + config SELFTEST_TALLOC bool "talloc() selftest" help diff --git a/test/self/Makefile b/test/self/Makefile index 2bfdbb9949df..2fecab1f8ee3 100644 --- a/test/self/Makefile +++ b/test/self/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_SELFTEST) += core.o obj-$(CONFIG_SELFTEST_BASE64) += base64.o obj-$(CONFIG_SELFTEST_RANGE) += range.o obj-$(CONFIG_SELFTEST_MALLOC) += malloc.o +obj-$(CONFIG_SELFTEST_CLEANUP) += cleanup.o obj-$(CONFIG_SELFTEST_TALLOC) += talloc.o obj-$(CONFIG_SELFTEST_PRINTF) += printf.o CFLAGS_printf.o += -Wno-format-security -Wno-format diff --git a/test/self/cleanup.c b/test/self/cleanup.c new file mode 100644 index 000000000000..7a3b63cc897f --- /dev/null +++ b/test/self/cleanup.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <bselftest.h> +#include <malloc.h> +#include <linux/err.h> +#include <linux/slab.h> + +BSELFTEST_GLOBALS(); + +static void *freed[2]; +static int frees; +static void count_free(void *mem) +{ + if (frees < ARRAY_SIZE(freed)) + freed[frees] = mem; + frees++; + free(mem); +} +DEFINE_FREE(count_free, void *, if (!IS_ERR_OR_NULL(_T)) count_free(_T)) + +static void test_cleanup(void) +{ + void *first, *second; + + /* leaving the scope must free the buffer, and only then */ + frees = 0; + { + void *p __free(count_free) = malloc(64); + + assert_cond(p != NULL); + assert_cond(frees == 0); + first = p; + } + assert_cond(frees == 1); + assert_cond(freed[0] == first); + /* the variable defined last is freed first */ + frees = 0; + { + void *p __free(count_free) = malloc(64); + void *q __free(count_free) = malloc(64); + + first = p; + second = q; + } + assert_cond(frees == 2); + assert_cond(freed[0] == second); + assert_cond(freed[1] == first); + + /* NULL must not reach the allocator */ + { + void *p __free(free) = NULL; + void *q __free(free_sensitive) = NULL; + void *r __free(kfree) = NULL; + void *s __free(kfree_sensitive) = NULL; + + assert_cond(p == NULL); + assert_cond(q == NULL); + assert_cond(r == NULL); + assert_cond(s == NULL); + } + + /* and neither may error pointers */ + { + void *p __free(free) = ERR_PTR(-EINVAL); + void *q __free(free_sensitive) = ERR_PTR(-EINVAL); + void *r __free(kfree) = ERR_PTR(-ENOMEM); + void *s __free(kfree_sensitive) = ERR_PTR(-ENOMEM); + + assert_cond(IS_ERR(p)); + assert_cond(IS_ERR(q)); + assert_cond(IS_ERR(r)); + assert_cond(IS_ERR(s)); + } + + /* no_free_ptr() inhibits the cleanup, so the buffer stays taken */ + { + void *p __free(kfree) = kmalloc(64, GFP_KERNEL); + assert_cond(p != NULL); + first = no_free_ptr(p); + } + second = kmalloc(64, GFP_KERNEL); + assert_cond(second != first); + kfree(first); + kfree(second); +} +bselftest(core, test_cleanup); -- 2.47.3
