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) {