aboutsummaryrefslogtreecommitdiffstats
path: root/libtest/libtest_fd_tracking.c
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 /libtest/libtest_fd_tracking.c
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 'libtest/libtest_fd_tracking.c')
-rw-r--r--libtest/libtest_fd_tracking.c157
1 files changed, 157 insertions, 0 deletions
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 */