commit:     61c865750c821381f2f8d3bc93b4e149127d2fdb
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Sat Jul 13 15:32:46 2019 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Sat Jul 13 15:32:46 2019 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=61c86575

libq/tree: ensure we don't work on garbage on sorted pkg trees

The contents of dirents come from a static buffer that may get
re-purposed when the next readdir call is made, so we cannot rely on it
staying around.  In particular on large directories, the entries will
get recycled, and hence garbage appear.  Thus, we need to copy the
entries, and free those copies.  The behaviour before
f855d0f4f7c3e6e570a1ad3dc98d737e78996e4a was actually using scandir
which allocates space for all dirents.  We now basically just copy the
bit we need, instead of the full dirent.

Thanks Sergei Trofimovich (slyfox) for digging into this case and
providing a VDB which displayed the problem.

Bug: https://bugs.gentoo.org/689290
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 libq/tree.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/libq/tree.c b/libq/tree.c
index 99e864b..0699334 100644
--- a/libq/tree.c
+++ b/libq/tree.c
@@ -344,6 +344,8 @@ tree_next_pkg_int(tree_cat_ctx *cat_ctx)
                        cat_ctx->pkg_cnt = 0;
                        cat_ctx->pkg_cur = 0;
                        while ((de = readdir(cat_ctx->dir)) != NULL) {
+                               char *name;
+
                                if (tree_filter_pkg(de) == 0)
                                        continue;
 
@@ -352,10 +354,13 @@ tree_next_pkg_int(tree_cat_ctx *cat_ctx)
                                        cat_ctx->pkg_ctxs = 
xrealloc(cat_ctx->pkg_ctxs,
                                                                
sizeof(*cat_ctx->pkg_ctxs) * pkg_size);
                                }
+                               name = xstrdup(de->d_name);
                                pkg_ctx = cat_ctx->pkg_ctxs[cat_ctx->pkg_cnt++] 
=
-                                       tree_open_pkg(cat_ctx, de->d_name);
-                               if (pkg_ctx == NULL)
+                                       tree_open_pkg(cat_ctx, name);
+                               if (pkg_ctx == NULL) {
+                                       free(name);
                                        cat_ctx->pkg_cnt--;
+                               }
                        }
 
                        if (cat_ctx->ctx->pkgsortfunc != NULL && 
cat_ctx->pkg_cnt > 1) {
@@ -948,6 +953,8 @@ tree_close_pkg(tree_pkg_ctx *pkg_ctx)
        /* avoid freeing tree_ctx' repo */
        if (pkg_ctx->cat_ctx->ctx->repo != pkg_ctx->repo)
                free(pkg_ctx->repo);
+       if (pkg_ctx->cat_ctx->ctx->do_sort)
+               free((char *)pkg_ctx->name);
        free(pkg_ctx->slot);
        free(pkg_ctx);
 }

Reply via email to