aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2026-05-14 11:59:34 +0200
committerMattias Andrée <m@maandree.se>2026-05-14 11:59:34 +0200
commita64be7b5310c10edb5adc9b25ea1785f629554fb (patch)
treebb421e63ee3f91effb0d4988d22ace6aae7282cf
parentTests and fixes (diff)
downloadlibrecrypt-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/Makefile1
-rw-r--r--libtest/alloc.c13
-rw-r--r--libtest/common.h9
-rw-r--r--libtest/libtest_check_no_leaks.c3
-rw-r--r--libtest/libtest_fd_tracking.c157
-rw-r--r--libtest/libtest_stack_on_signal.c8
-rw-r--r--libtest/libtest_start_tracking.c1
-rw-r--r--libtest/libtest_stop_tracking.c1
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);
}