commit:     77a0017b2ea736670eba1f3923201dc1c2858feb
Author:     Peter Stuge <peter <AT> stuge <DOT> se>
AuthorDate: Tue Nov 10 19:01:44 2020 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Sat Nov 14 16:44:47 2020 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=77a0017b

qmerge: copy tbz2 archive into decompressor/tar pipe ourselves

We have nothing to do while tar is running so let's copy the tbz2
archive part into a popen():ed tar pipe ourselves, rather than depend
on an external program.

Doing so also fixes a possible shell injection vulnerability and
improves performance.

Fixes: 7171479f ("qmerge: unpack tbz2 file without splitting it first", 
2020-05-17)
Signed-off-by: Peter Stuge <peter <AT> stuge.se>
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 qmerge.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 49 insertions(+), 3 deletions(-)

diff --git a/qmerge.c b/qmerge.c
index 36d3596..6d98329 100644
--- a/qmerge.c
+++ b/qmerge.c
@@ -1231,13 +1231,59 @@ pkg_merge(int level, const depend_atom *atom, const 
struct pkg_t *pkg)
         * Since some tools (e.g. zstd) complain about the .bz2
         * extension, we feed the tool by input redirection. */
        snprintf(buf, sizeof(buf),
-               BUSYBOX " sh -c 'dd status=none if=%s bs=1 count=%d %s%s | "
+               BUSYBOX " sh -c '%s%s"
                "tar -x%sf - -C image/'",
-               tbz2, tbz2size, compr[0] == '\0' ? "" : "| ", compr,
+               compr, compr[0] == '\0' ? "" : " | ",
                ((verbose > 1) ? "v" : ""));
 
+       /* start the tar pipe and copy tbz2size binpkg bytes into it
+        * "manually" rather than depending on dd or head */
+       {
+               FILE *tarpipe, *tbz2f;
+               unsigned char iobuf[8192];
+               int piped = 0;
+               size_t n, rd, wr;
+
+               tarpipe = popen(buf, "w");
+               if (NULL == tarpipe)
+                       errp("popen(%s)", buf);
+
+               tbz2f = fopen(tbz2, "r");
+               if (NULL == tbz2f)
+                       errp("fopen(%s)", tbz2);
+
+               for (piped = wr = 0; piped < tbz2size; piped += wr) {
+                       n = MIN(tbz2size - piped, (ssize_t)sizeof iobuf);
+                       rd = fread(iobuf, 1, n, tbz2f);
+                       if (0 == rd) {
+                               errno = ferror(tbz2f);
+                               if (errno)
+                                       errp("fread(%s)", tbz2);
+
+                               if (feof(tbz2f))
+                                       err("%s: unexpected EOF; corrupted 
binpkg", tbz2);
+                       }
+
+                       for (wr = n = 0; wr < rd; wr += n) {
+                               n = fwrite(iobuf + wr, 1, rd - wr, tarpipe);
+                               if (n != rd - wr) {
+                                       errno = ferror(tarpipe);
+                                       if (errno)
+                                               errp("fwrite(%s)", buf);
+
+                                       if (feof(tarpipe))
+                                               err("%s pipe: unexpected EOF", 
buf);
+                               }
+                       }
+               }
+
+               fclose(tbz2f);
+
+               if (-1 == pclose(tarpipe))
+                       errp("pclose(%s)", buf);
+       }
+
        free(tbz2);
-       xsystem(buf);
        fflush(stdout);
 
        eat_file("vdb/DEFINED_PHASES", &phases, &phases_len);

Reply via email to