Blob Blame History Raw
From 29328cbda5daea22f02e4ca94ca75783f22efc61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Sun, 29 Sep 2013 14:40:58 +0200
Subject: [PATCH] Fix buffer overrun when enumerating files

https://bugs.freedesktop.org/show_bug.cgi?id=69887

Based-on-a-patch-by: Hans Petter Jansson <hpj@copyleft.no>
---
 src/shared/util.c    | 79 +++++++++++++++++-----------------------------------
 src/test/test-util.c | 10 +++++++
 2 files changed, 36 insertions(+), 53 deletions(-)

diff --git a/src/shared/util.c b/src/shared/util.c
index 766957a..4f80cc8 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -4412,38 +4412,31 @@ int dirent_ensure_type(DIR *d, struct dirent *de) {
 }
 
 int in_search_path(const char *path, char **search) {
-        char **i, *parent;
+        char **i;
+        _cleanup_free_ char *parent = NULL;
         int r;
 
         r = path_get_parent(path, &parent);
         if (r < 0)
                 return r;
 
-        r = 0;
-
-        STRV_FOREACH(i, search) {
-                if (path_equal(parent, *i)) {
-                        r = 1;
-                        break;
-                }
-        }
-
-        free(parent);
+        STRV_FOREACH(i, search)
+                if (path_equal(parent, *i))
+                        return 1;
 
-        return r;
+        return 0;
 }
 
 int get_files_in_directory(const char *path, char ***list) {
-        DIR *d;
-        int r = 0;
-        unsigned n = 0;
-        char **l = NULL;
+        _cleanup_closedir_ DIR *d = NULL;
+        size_t bufsize = 0, n = 0;
+        _cleanup_strv_free_ char **l = NULL;
 
         assert(path);
 
         /* Returns all files in a directory in *list, and the number
          * of files as return value. If list is NULL returns only the
-         * number */
+         * number. */
 
         d = opendir(path);
         if (!d)
@@ -4455,11 +4448,9 @@ int get_files_in_directory(const char *path, char ***list) {
                 int k;
 
                 k = readdir_r(d, &buf.de, &de);
-                if (k != 0) {
-                        r = -k;
-                        goto finish;
-                }
-
+                assert(k >= 0);
+                if (k > 0)
+                        return -k;
                 if (!de)
                         break;
 
@@ -4469,43 +4460,25 @@ int get_files_in_directory(const char *path, char ***list) {
                         continue;
 
                 if (list) {
-                        if ((unsigned) r >= n) {
-                                char **t;
-
-                                n = MAX(16, 2*r);
-                                t = realloc(l, sizeof(char*) * n);
-                                if (!t) {
-                                        r = -ENOMEM;
-                                        goto finish;
-                                }
-
-                                l = t;
-                        }
-
-                        assert((unsigned) r < n);
+                        /* one extra slot is needed for the terminating NULL */
+                        if (!GREEDY_REALLOC(l, bufsize, n + 2))
+                                return -ENOMEM;
 
-                        l[r] = strdup(de->d_name);
-                        if (!l[r]) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
+                        l[n] = strdup(de->d_name);
+                        if (!l[n])
+                                return -ENOMEM;
 
-                        l[++r] = NULL;
+                        l[++n] = NULL;
                 } else
-                        r++;
+                        n++;
         }
 
-finish:
-        if (d)
-                closedir(d);
-
-        if (r >= 0) {
-                if (list)
-                        *list = l;
-        } else
-                strv_free(l);
+        if (list) {
+                *list = l;
+                l = NULL; /* avoid freeing */
+        }
 
-        return r;
+        return n;
 }
 
 char *strjoin(const char *x, ...) {
diff --git a/src/test/test-util.c b/src/test/test-util.c
index ad13d53..c5762ed 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -27,6 +27,7 @@
 #include <errno.h>
 
 #include "util.h"
+#include "strv.h"
 
 static void test_streq_ptr(void) {
         assert_se(streq_ptr(NULL, NULL));
@@ -582,6 +583,14 @@ static void test_fstab_node_to_udev_node(void) {
         free(n);
 }
 
+static void test_get_files_in_directory(void) {
+        _cleanup_strv_free_ char **l = NULL, **t = NULL;
+
+        assert_se(get_files_in_directory("/tmp", &l) >= 0);
+        assert_se(get_files_in_directory(".", &l) >= 0);
+        assert_se(get_files_in_directory(".", NULL) >= 0);
+}
+
 int main(int argc, char *argv[]) {
         test_streq_ptr();
         test_first_word();
@@ -618,6 +627,7 @@ int main(int argc, char *argv[]) {
         test_parse_user_at_host();
         test_split_pair();
         test_fstab_node_to_udev_node();
+        test_get_files_in_directory();
 
         return 0;
 }