This splits gzip into two processes, such that the heavy lifting is done in a
process with even fewer privileges. The idea is the child decompresses the
data and feeds it to the parent over a pipe. There's also a control pipe used
for some metadata that needs to be passed up.

early version, i'm not sure it's entirely reliable. it does seem to decompress
some test files, however.


Index: gzopen.c
===================================================================
RCS file: /cvs/src/usr.bin/compress/gzopen.c,v
retrieving revision 1.34
diff -u -p -r1.34 gzopen.c
--- gzopen.c    3 Sep 2016 12:29:30 -0000       1.34
+++ gzopen.c    3 Sep 2016 15:04:26 -0000
@@ -92,6 +92,8 @@ struct gz_stream {
        u_int32_t z_hlen;       /* length of the gz header */
        u_int64_t z_total_in;   /* # bytes in */
        u_int64_t z_total_out;  /* # bytes out */
+       int     z_pipe[2];
+       int     z_ctlpipe[2];
 } gz_stream;
 
 static const u_char gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
@@ -100,10 +102,13 @@ static u_int32_t get_int32(gz_stream *);
 static int get_header(gz_stream *, char *, int);
 static int get_byte(gz_stream *);
 
+static void childcopier(gz_stream *, char *, int);
+
 void *
 gz_ropen(int fd, char *name, int gotmagic)
 {
        gz_stream *s;
+       int ok;
 
        if (fd < 0)
                return NULL;
@@ -126,20 +131,31 @@ gz_ropen(int fd, char *name, int gotmagi
        s->z_crc = crc32(0L, Z_NULL, 0);
        s->z_mode = 'r';
 
-       if (inflateInit2(&(s->z_stream), -MAX_WBITS) != Z_OK) {
-               free (s);
+       if (pipe(s->z_pipe) || pipe(s->z_ctlpipe))
                return NULL;
-       }
-       s->z_stream.next_in = s->z_buf;
-       s->z_stream.avail_out = Z_BUFSIZE;
 
        errno = 0;
        s->z_fd = fd;
 
-       /* read the .gz header */
-       if (get_header(s, name, gotmagic) != 0) {
-               gz_close(s, NULL, NULL, NULL);
-               s = NULL;
+       switch(fork()) {
+       case -1:
+               free(s);
+               return NULL;
+       case 0:
+               close(s->z_pipe[1]);
+               close(s->z_ctlpipe[1]);
+               childcopier(s, name, gotmagic);
+               break;
+       default:
+               close(s->z_pipe[0]);
+               close(s->z_ctlpipe[0]);
+               break;
+       }
+
+       if (read(s->z_ctlpipe[1], &ok, sizeof(ok)) != sizeof(ok) ||
+           ok != 0) {
+               free(s);
+               return NULL;
        }
 
        return s;
@@ -260,6 +276,26 @@ int
 gz_read(void *cookie, char *buf, int len)
 {
        gz_stream *s = (gz_stream*)cookie;
+       int amt;
+       int ok;
+
+       amt = read(s->z_pipe[1], buf, len);
+       if (amt == -1)
+               return -1;
+       
+       if (amt > 0)
+               return amt;
+       
+       if (read(s->z_ctlpipe[1], &ok, sizeof(ok)) != sizeof(ok) ||
+           ok != 0) {
+               return -1;
+       }
+}
+
+int
+do_gz_read(void *cookie, char *buf, int len)
+{
+       gz_stream *s = (gz_stream*)cookie;
        u_char *start = buf; /* starting point for crc computation */
        int error = Z_OK;
 
@@ -330,6 +366,62 @@ bad:
        return (-1);
 }
 
+static void
+childcopier(gz_stream *s, char *name, int gotmagic)
+{
+       struct z_info info;
+       char tmpbuffer[Z_BUFSIZE];
+       int one = 1;
+       int zero = 0;
+       int amt;
+
+       if (pledge("stdio", NULL) == -1)
+               goto die;
+
+       if (inflateInit2(&(s->z_stream), -MAX_WBITS) != Z_OK) {
+               goto die;
+       }
+       s->z_stream.next_in = s->z_buf;
+       s->z_stream.avail_out = Z_BUFSIZE;
+
+       /* read the .gz header */
+       if (get_header(s, name, gotmagic) != 0) {
+               goto die;
+       }
+
+       write(s->z_ctlpipe[0], &zero, sizeof(zero));
+
+       while (1) {
+               amt = do_gz_read(s, tmpbuffer, sizeof(tmpbuffer));
+               if (amt == -1)
+                       goto die;
+               if (amt == 0)
+                       break;
+               while (amt > 0) {
+                       int x = write(s->z_pipe[0], tmpbuffer, amt);
+                       if (x < 1)
+                               goto die;
+                       amt -= x;
+               }
+       }
+       close(s->z_pipe[0]);
+       write(s->z_ctlpipe[0], &zero, sizeof(zero));
+
+       info.mtime = s->z_time;
+       info.crc = s->z_crc;
+       info.hlen = s->z_hlen;
+       info.total_in = s->z_total_in;
+       info.total_out = s->z_total_out;
+
+       write(s->z_ctlpipe[0], &info, sizeof(info));
+
+       _exit(0);
+
+die:
+       write(s->z_ctlpipe[0], &one, sizeof(one));
+       _exit(1);
+}
+
 #ifndef SMALL
 static int
 put_int32(gz_stream *s, u_int32_t x)
@@ -493,37 +585,35 @@ gz_close(void *cookie, struct z_info *in
                return -1;
 
 #ifndef SMALL
-       if (s->z_mode == 'w' && (err = gz_flush (s, Z_FINISH)) == Z_OK) {
-               if ((err = put_int32 (s, s->z_crc)) == Z_OK) {
-                       s->z_hlen += sizeof(int32_t);
-                       if ((err = put_int32 (s, s->z_stream.total_in)) == Z_OK)
+       if (s->z_mode == 'w') {
+               if ((err = gz_flush (s, Z_FINISH)) == Z_OK) {
+                       if ((err = put_int32 (s, s->z_crc)) == Z_OK) {
                                s->z_hlen += sizeof(int32_t);
+                               if ((err = put_int32 (s, s->z_stream.total_in)) 
== Z_OK)
+                                       s->z_hlen += sizeof(int32_t);
+                       }
                }
-       }
-#endif
-       if (!err && s->z_stream.state != NULL) {
-               if (s->z_mode == 'w')
-#ifndef SMALL
+               if (!err && s->z_stream.state != NULL)
                        err = deflateEnd(&s->z_stream);
-#else
-                       err = -1;
-#endif
-               else if (s->z_mode == 'r')
-                       err = inflateEnd(&s->z_stream);
-       }
+               if (info != NULL) {
+                       info->mtime = s->z_time;
+                       info->crc = s->z_crc;
+                       info->hlen = s->z_hlen;
+                       if (s->z_mode == 'r') {
+                               info->total_in = s->z_total_in;
+                               info->total_out = s->z_total_out;
+                       } else {
+                               info->total_in = s->z_stream.total_in;
+                               info->total_out = s->z_stream.total_out;
+                       }
 
-       if (info != NULL) {
-               info->mtime = s->z_time;
-               info->crc = s->z_crc;
-               info->hlen = s->z_hlen;
-               if (s->z_mode == 'r') {
-                       info->total_in = s->z_total_in;
-                       info->total_out = s->z_total_out;
-               } else {
-                       info->total_in = s->z_stream.total_in;
-                       info->total_out = s->z_stream.total_out;
                }
-
+       } else
+#endif
+       if (s->z_mode == 'r') {
+               if (info != NULL)
+                       if (read(s->z_ctlpipe[1], info, sizeof(*info)) != 
sizeof(*info))
+                               err = -1;
        }
 
        setfile(name, s->z_fd, sb);
@@ -536,4 +626,3 @@ gz_close(void *cookie, struct z_info *in
 
        return err;
 }
-
Index: main.c
===================================================================
RCS file: /cvs/src/usr.bin/compress/main.c,v
retrieving revision 1.94
diff -u -p -r1.94 main.c
--- main.c      3 Sep 2016 13:26:50 -0000       1.94
+++ main.c      3 Sep 2016 15:04:26 -0000
@@ -166,7 +166,7 @@ main(int argc, char *argv[])
        char outfile[PATH_MAX], _infile[PATH_MAX], suffix[16];
        int bits, ch, error, rc, cflag, oflag;
 
-       if (pledge("stdio rpath wpath cpath fattr chown", NULL) == -1)
+       if (pledge("stdio rpath wpath cpath fattr chown proc", NULL) == -1)
                err(1, "pledge");
 
        bits = cflag = oflag = 0;
@@ -332,7 +332,7 @@ main(int argc, char *argv[])
        argv += optind;
 
        if (cflag || testmode || (!oflag && argc == 0))
-               if (pledge("stdio rpath", NULL) == -1)
+               if (pledge("stdio rpath proc", NULL) == -1)
                        err(1, "pledge");
 
        if (argc == 0) {

Reply via email to