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); }
