diff options
| author | Mattias Andrée <m@maandree.se> | 2026-05-14 11:59:34 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-05-14 11:59:34 +0200 |
| commit | a64be7b5310c10edb5adc9b25ea1785f629554fb (patch) | |
| tree | bb421e63ee3f91effb0d4988d22ace6aae7282cf /libtest | |
| parent | Tests and fixes (diff) | |
| download | librecrypt-a64be7b5310c10edb5adc9b25ea1785f629554fb.tar.gz librecrypt-a64be7b5310c10edb5adc9b25ea1785f629554fb.tar.bz2 librecrypt-a64be7b5310c10edb5adc9b25ea1785f629554fb.tar.xz | |
Fix libtest and add file descriptor leak detection
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to '')
| -rw-r--r-- | libtest/Makefile | 1 | ||||
| -rw-r--r-- | libtest/alloc.c | 13 | ||||
| -rw-r--r-- | libtest/common.h | 9 | ||||
| -rw-r--r-- | libtest/libtest_check_no_leaks.c | 3 | ||||
| -rw-r--r-- | libtest/libtest_fd_tracking.c | 157 | ||||
| -rw-r--r-- | libtest/libtest_stack_on_signal.c | 8 | ||||
| -rw-r--r-- | libtest/libtest_start_tracking.c | 1 | ||||
| -rw-r--r-- | libtest/libtest_stop_tracking.c | 1 |
8 files changed, 187 insertions, 6 deletions
diff --git a/libtest/Makefile b/libtest/Makefile index 75a76cd..952eabf 100644 --- a/libtest/Makefile +++ b/libtest/Makefile @@ -28,6 +28,7 @@ OBJ =\ libtest_stack_on_signal.o\ libtest_get_alloc_failure_in.o\ libtest_set_alloc_failure_in.o\ + libtest_fd_tracking.o\ random.o\ $(OBJ_BACKTRACE)\ diff --git a/libtest/alloc.c b/libtest/alloc.c index e154624..ab140a2 100644 --- a/libtest/alloc.c +++ b/libtest/alloc.c @@ -726,14 +726,17 @@ check_successfuls(void) size_t pagesize; char *s; wchar_t *w; - void *q; + size_t zu; check(1); check(0); pagesize = libtest_get_pagesize(); + zu = libtest_get_alloc_failure_in(); + libtest_set_alloc_failure_in(0u); libtest_stop_tracking(); + libtest_set_alloc_failure_in(zu); p = valloc(6u); assert(p); @@ -751,7 +754,10 @@ check_successfuls(void) assert(malloc_usable_size(p) >= pagesize); /* cannot be free(3)ed */ + zu = libtest_get_alloc_failure_in(); + libtest_set_alloc_failure_in(0u); libtest_start_tracking(); + libtest_set_alloc_failure_in(zu); s = strdup("test string"); assert(s); @@ -820,8 +826,7 @@ check_successfuls(void) assert(p != MAP_FAILED); p = mremap(p, 1u, 2u, 0); assert(p != MAP_FAILED); - assert(malloc_usable_size(p) >= 6u); - asser(!munmap(p, 2u)); + assert(!munmap(p, 2u)); } @@ -974,7 +979,7 @@ main(void) check_successfuls(); libtest_set_alloc_failure_in(1000u); check_successfuls(); - EXPECT(libtest_get_alloc_failure_in() == 1000u - 31u); + EXPECT(libtest_get_alloc_failure_in() == 1000u - 33u); check_failures(); p = NULL; diff --git a/libtest/common.h b/libtest/common.h index 8d22b5b..54456d2 100644 --- a/libtest/common.h +++ b/libtest/common.h @@ -9,6 +9,7 @@ # include <sys/syscall.h> #endif #include <sys/mman.h> +#include <dirent.h> #include <errno.h> #include <inttypes.h> #include <limits.h> @@ -179,6 +180,14 @@ HIDDEN void *libtest_alloc(struct meminfo *); HIDDEN void libtest_free(void *, enum libtest_zero_check); HIDDEN int libtest_check_custom_mmap(void); + +/** + * action=1 mean start/resume tracking; + * action=0 mean suspend tracking; + * action=-1 mean stop tracking and report resource leaks + */ +HIDDEN int libtest_fd_tracking(int action); + #ifdef WITH_BACKTRACE HIDDEN void libtest_print_backtrace(FILE *, const char *prefix, const char *indent, size_t first, const struct backtrace *, ucontext_t *); diff --git a/libtest/libtest_check_no_leaks.c b/libtest/libtest_check_no_leaks.c index 40e3a80..aa99377 100644 --- a/libtest/libtest_check_no_leaks.c +++ b/libtest/libtest_check_no_leaks.c @@ -46,8 +46,7 @@ out: int libtest_check_no_leaks(void) { - /* TODO check file descriptor leaks */ - return check_no_memory_leaks(); + return libtest_fd_tracking(-1) & check_no_memory_leaks(); } diff --git a/libtest/libtest_fd_tracking.c b/libtest/libtest_fd_tracking.c new file mode 100644 index 0000000..d3c766c --- /dev/null +++ b/libtest/libtest_fd_tracking.c @@ -0,0 +1,157 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +struct fd { + int name; + int leakable; +}; + +static size_t nopened = 0u; +static struct fd *opened = NULL; + + +static int +cmp_fd(const void *av, const void *bv) +{ + const struct fd *a = av; + const struct fd *b = bv; + return a->name - b->name; +} + + +static char * +get_path(int fd) +{ + char path[sizeof("/dev/fd/-") + 3u * sizeof(int)]; + int pathlen; + char *ret = NULL; + size_t size = 0; + ssize_t r; + + pathlen = sprintf(path, "/dev/fd/%i", fd); + assert(pathlen > (int)sizeof("/dev/fd/") - 1); + assert((size_t)pathlen < sizeof(path)); + + do { + ret = realloc(ret, size += 128u); + assert(ret); + r = readlink(path, ret, size - 1u); + assert(r >= 0); + if (r < 0) + return NULL; + } while ((size_t)r >= size - 2u); + + ret[r] = '\0'; + return ret; + +} + + +int +libtest_fd_tracking(int action) +{ + size_t new_nopened = 0u; + struct fd *new_opened = NULL; + DIR *dir; + struct dirent *f; + int ret = 1, dfd, name, digit; + int accept_memleak = libtest_malloc_accept_leakage; + size_t i, j; + char *path; + + /* so libtest doesn't complain about us not zeroing before freeing, + * and so it will not report memory leaks in fprintf from our + * resource leak report */ + libtest_malloc_accept_leakage = 1; + + dir = opendir("/dev/fd/"); + assert(dir != NULL); + dfd = dirfd(dir); +next: + while ((f = readdir(dir))) { + name = 0; + for (i = 0u; f->d_name[i]; i++) { + if ('0' > f->d_name[i] || f->d_name[i] > '9') + goto next; + digit = (int)f->d_name[i] - (int)'0'; + if (name > (INT_MAX - digit) / 10) + goto next; + name = name * 10 + digit; + } + if (name == dfd) + continue; + + new_opened = realloc(new_opened, (new_nopened + 1u) * sizeof(*new_opened)); + new_opened[new_nopened].name = name; + new_opened[new_nopened].leakable = action; + new_nopened += 1u; + } + closedir(dir); + + qsort(new_opened, new_nopened, sizeof(*new_opened), &cmp_fd); + + for (i = j = 0u; i < new_nopened || j < nopened;) { + if (i == new_nopened) { + old_unique: + /* the file has been closed, that's great! */ + j++; + } else if (j == nopened) { + new_unique: + if (action < 0) { + ret = 0; + path = get_path(new_opened[i].name); + if (path) + fprintf(stderr, "File descriptor leak: %i (%s)\n", new_opened[i].name, path); + else + fprintf(stderr, "File descriptor leak: %i\n", new_opened[i].name); + free(path); + } + i++; + } else if (new_opened[i].name < opened[j].name) { + goto new_unique; + } else if (new_opened[i].name > opened[j].name) { + goto old_unique; + } else { + new_opened[i].leakable = opened[j].leakable; + i++; + j++; + } + } + + if (new_nopened == 0u || action < 0) { + free(new_opened); + new_opened = NULL; + new_nopened = 0u; + } + + if (action >= 0) { + opened = new_opened; + nopened = new_nopened; + } else { + free(opened); + opened = NULL; + nopened = 0u; + } + + libtest_malloc_accept_leakage = accept_memleak; + return ret; +} + + +#else + + +int +main(void) +{ + SET_UP_ALARM(); + + return 0; +} + + +#endif +/* TODO maybe test */ +/* TODO maybe attempt to tracking where files are opened */ diff --git a/libtest/libtest_stack_on_signal.c b/libtest/libtest_stack_on_signal.c index 0f92972..f10155a 100644 --- a/libtest/libtest_stack_on_signal.c +++ b/libtest/libtest_stack_on_signal.c @@ -62,6 +62,14 @@ libtest_stack_on_signal(int signo, struct sigaction *old_out) void libtest_stop_stack_on_signal(int signo, const struct sigaction *old) { + struct sigaction sa; + + if (!old) { + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + old = &sa; + } + assert(!sigaction(signo, old, NULL)); destroy_altstack(); } diff --git a/libtest/libtest_start_tracking.c b/libtest/libtest_start_tracking.c index 070fab9..df11146 100644 --- a/libtest/libtest_start_tracking.c +++ b/libtest/libtest_start_tracking.c @@ -7,6 +7,7 @@ void libtest_start_tracking(void) { libtest_malloc_accept_leakage = 0; + libtest_fd_tracking(1); } diff --git a/libtest/libtest_stop_tracking.c b/libtest/libtest_stop_tracking.c index 29bb2aa..5faddb2 100644 --- a/libtest/libtest_stop_tracking.c +++ b/libtest/libtest_stop_tracking.c @@ -7,6 +7,7 @@ void libtest_stop_tracking(void) { libtest_malloc_accept_leakage = 1; + libtest_fd_tracking(0); } |
