aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2022-11-22 12:10:18 +0100
committerDmitry Vyukov <dvyukov@google.com>2022-11-23 09:09:39 +0100
commitf46b2272283056876fb9df794e3c67d3a2c3c059 (patch)
tree3e6574e80ea6d8b11142627835a740074ec3449e
parent1c50689b39eb028d91a28951b8300b7970a2aca5 (diff)
executor: add test for zlib decompression
-rw-r--r--executor/common_test.h22
-rw-r--r--pkg/csource/generated.go389
-rw-r--r--pkg/ipc/ipc_test.go38
-rw-r--r--pkg/testutil/testutil.go8
-rw-r--r--prog/compression_test.go10
-rw-r--r--sys/test/exec.txt1
6 files changed, 459 insertions, 9 deletions
diff --git a/executor/common_test.h b/executor/common_test.h
index 9d9adc36f..2ce410b36 100644
--- a/executor/common_test.h
+++ b/executor/common_test.h
@@ -101,6 +101,28 @@ static long syz_compare_int(volatile long n, ...)
}
#endif
+#if SYZ_EXECUTOR || __NR_syz_compare_zlib
+#include "common_zlib.h"
+#include <errno.h>
+#include <fcntl.h>
+
+// syz_compare_zlib(data ptr[in, array[int8]], size bytesize[data], zdata ptr[in, compressed_image], zsize bytesize[zdata])
+static long syz_compare_zlib(volatile long data, volatile long size, volatile long zdata, volatile long zsize)
+{
+ int fd = open("./uncompressed", O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (fd == -1)
+ return -1;
+ if (ftruncate(fd, size))
+ return -1;
+ if (puff_zlib_to_file((unsigned char*)zdata, zsize, fd, size))
+ return -1;
+ void* uncompressed = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (uncompressed == MAP_FAILED)
+ return -1;
+ return syz_compare(data, size, (long)uncompressed, size);
+}
+#endif
+
#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE
static void loop();
static int do_sandbox_none(void)
diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go
index 9e2fa9155..8e09ba85a 100644
--- a/pkg/csource/generated.go
+++ b/pkg/csource/generated.go
@@ -11489,6 +11489,395 @@ static long syz_compare_int(volatile long n, ...)
}
#endif
+#if SYZ_EXECUTOR || __NR_syz_compare_zlib
+
+//% This code is derived from puff.{c,h}, found in the zlib development. The
+//% original files come with the following copyright notice:
+
+//% Copyright (C) 2002-2013 Mark Adler, all rights reserved
+//% version 2.3, 21 Jan 2013
+//% This software is provided 'as-is', without any express or implied
+//% warranty. In no event will the author be held liable for any damages
+//% arising from the use of this software.
+//% Permission is granted to anyone to use this software for any purpose,
+//% including commercial applications, and to alter it and redistribute it
+//% freely, subject to the following restrictions:
+//% 1. The origin of this software must not be misrepresented; you must not
+//% claim that you wrote the original software. If you use this software
+//% in a product, an acknowledgment in the product documentation would be
+//% appreciated but is not required.
+//% 2. Altered source versions must be plainly marked as such, and must not be
+//% misrepresented as being the original software.
+//% 3. This notice may not be removed or altered from any source distribution.
+//% Mark Adler madler@alumni.caltech.edu
+
+//% BEGIN CODE DERIVED FROM puff.{c,h}
+
+#include <setjmp.h>
+#define MAXBITS 15
+#define MAXLCODES 286
+#define MAXDCODES 30
+#define MAXCODES (MAXLCODES + MAXDCODES)
+#define FIXLCODES 288
+
+struct puff_state {
+ unsigned char* out;
+ unsigned long outlen;
+ unsigned long outcnt;
+ const unsigned char* in;
+ unsigned long inlen;
+ unsigned long incnt;
+ int bitbuf;
+ int bitcnt;
+ jmp_buf env;
+};
+static int puff_bits(struct puff_state* s, int need)
+{
+ long val;
+ val = s->bitbuf;
+ while (s->bitcnt < need) {
+ if (s->incnt == s->inlen)
+ longjmp(s->env, 1);
+ val |= (long)(s->in[s->incnt++]) << s->bitcnt;
+ s->bitcnt += 8;
+ }
+ s->bitbuf = (int)(val >> need);
+ s->bitcnt -= need;
+ return (int)(val & ((1L << need) - 1));
+}
+static int puff_stored(struct puff_state* s)
+{
+ unsigned len;
+ s->bitbuf = 0;
+ s->bitcnt = 0;
+ if (s->incnt + 4 > s->inlen)
+ return 2;
+ len = s->in[s->incnt++];
+ len |= s->in[s->incnt++] << 8;
+ if (s->in[s->incnt++] != (~len & 0xff) ||
+ s->in[s->incnt++] != ((~len >> 8) & 0xff))
+ return -2;
+ if (s->incnt + len > s->inlen)
+ return 2;
+ if (s->out != (unsigned char*)0) {
+ if (s->outcnt + len > s->outlen)
+ return 1;
+ while (len--)
+ s->out[s->outcnt++] = s->in[s->incnt++];
+ } else {
+ s->outcnt += len;
+ s->incnt += len;
+ }
+ return 0;
+}
+struct puff_huffman {
+ short* count;
+ short* symbol;
+};
+static int puff_decode(struct puff_state* s, const struct puff_huffman* h)
+{
+ int len;
+ int code;
+ int first;
+ int count;
+ int index;
+ int bitbuf;
+ int left;
+ short* next;
+
+ bitbuf = s->bitbuf;
+ left = s->bitcnt;
+ code = first = index = 0;
+ len = 1;
+ next = h->count + 1;
+ while (1) {
+ while (left--) {
+ code |= bitbuf & 1;
+ bitbuf >>= 1;
+ count = *next++;
+ if (code - count < first) {
+ s->bitbuf = bitbuf;
+ s->bitcnt = (s->bitcnt - len) & 7;
+ return h->symbol[index + (code - first)];
+ }
+ index += count;
+ first += count;
+ first <<= 1;
+ code <<= 1;
+ len++;
+ }
+ left = (MAXBITS + 1) - len;
+ if (left == 0)
+ break;
+ if (s->incnt == s->inlen)
+ longjmp(s->env, 1);
+ bitbuf = s->in[s->incnt++];
+ if (left > 8)
+ left = 8;
+ }
+ return -10;
+}
+static int puff_construct(struct puff_huffman* h, const short* length, int n)
+{
+ int symbol;
+ int len;
+ int left;
+ short offs[MAXBITS + 1];
+ for (len = 0; len <= MAXBITS; len++)
+ h->count[len] = 0;
+ for (symbol = 0; symbol < n; symbol++)
+ (h->count[length[symbol]])++;
+ if (h->count[0] == n)
+ return 0;
+ left = 1;
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= h->count[len];
+ if (left < 0)
+ return left;
+ }
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ offs[len + 1] = offs[len] + h->count[len];
+ for (symbol = 0; symbol < n; symbol++)
+ if (length[symbol] != 0)
+ h->symbol[offs[length[symbol]]++] = symbol;
+ return left;
+}
+static int puff_codes(struct puff_state* s,
+ const struct puff_huffman* lencode,
+ const struct puff_huffman* distcode)
+{
+ int symbol;
+ int len;
+ unsigned dist;
+ static const short lens[29] = {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258};
+ static const short lext[29] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
+ static const short dists[30] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+ static const short dext[30] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+ do {
+ symbol = puff_decode(s, lencode);
+ if (symbol < 0)
+ return symbol;
+ if (symbol < 256) {
+ if (s->out != (unsigned char*)0) {
+ if (s->outcnt == s->outlen)
+ return 1;
+ s->out[s->outcnt] = symbol;
+ }
+ s->outcnt++;
+ } else if (symbol > 256) {
+ symbol -= 257;
+ if (symbol >= 29)
+ return -10;
+ len = lens[symbol] + puff_bits(s, lext[symbol]);
+ symbol = puff_decode(s, distcode);
+ if (symbol < 0)
+ return symbol;
+ dist = dists[symbol] + puff_bits(s, dext[symbol]);
+ if (dist > s->outcnt)
+ return -11;
+ if (s->out != (unsigned char*)0) {
+ if (s->outcnt + len > s->outlen)
+ return 1;
+ while (len--) {
+ s->out[s->outcnt] =
+ dist > s->outcnt ? 0 : s->out[s->outcnt - dist];
+ s->outcnt++;
+ }
+ } else
+ s->outcnt += len;
+ }
+ } while (symbol != 256);
+ return 0;
+}
+static int puff_fixed(struct puff_state* s)
+{
+ static int virgin = 1;
+ static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
+ static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
+ static struct puff_huffman lencode, distcode;
+ if (virgin) {
+ int symbol;
+ short lengths[FIXLCODES];
+ lencode.count = lencnt;
+ lencode.symbol = lensym;
+ distcode.count = distcnt;
+ distcode.symbol = distsym;
+ for (symbol = 0; symbol < 144; symbol++)
+ lengths[symbol] = 8;
+ for (; symbol < 256; symbol++)
+ lengths[symbol] = 9;
+ for (; symbol < 280; symbol++)
+ lengths[symbol] = 7;
+ for (; symbol < FIXLCODES; symbol++)
+ lengths[symbol] = 8;
+ puff_construct(&lencode, lengths, FIXLCODES);
+ for (symbol = 0; symbol < MAXDCODES; symbol++)
+ lengths[symbol] = 5;
+ puff_construct(&distcode, lengths, MAXDCODES);
+ virgin = 0;
+ }
+ return puff_codes(s, &lencode, &distcode);
+}
+static int puff_dynamic(struct puff_state* s)
+{
+ int nlen, ndist, ncode;
+ int index;
+ int err;
+ short lengths[MAXCODES];
+ short lencnt[MAXBITS + 1], lensym[MAXLCODES];
+ short distcnt[MAXBITS + 1], distsym[MAXDCODES];
+ struct puff_huffman lencode, distcode;
+ static const short order[19] =
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+ lencode.count = lencnt;
+ lencode.symbol = lensym;
+ distcode.count = distcnt;
+ distcode.symbol = distsym;
+ nlen = puff_bits(s, 5) + 257;
+ ndist = puff_bits(s, 5) + 1;
+ ncode = puff_bits(s, 4) + 4;
+ if (nlen > MAXLCODES || ndist > MAXDCODES)
+ return -3;
+ for (index = 0; index < ncode; index++)
+ lengths[order[index]] = puff_bits(s, 3);
+ for (; index < 19; index++)
+ lengths[order[index]] = 0;
+ err = puff_construct(&lencode, lengths, 19);
+ if (err != 0)
+ return -4;
+ index = 0;
+ while (index < nlen + ndist) {
+ int symbol;
+ int len;
+
+ symbol = puff_decode(s, &lencode);
+ if (symbol < 0)
+ return symbol;
+ if (symbol < 16)
+ lengths[index++] = symbol;
+ else {
+ len = 0;
+ if (symbol == 16) {
+ if (index == 0)
+ return -5;
+ len = lengths[index - 1];
+ symbol = 3 + puff_bits(s, 2);
+ } else if (symbol == 17)
+ symbol = 3 + puff_bits(s, 3);
+ else
+ symbol = 11 + puff_bits(s, 7);
+ if (index + symbol > nlen + ndist)
+ return -6;
+ while (symbol--)
+ lengths[index++] = len;
+ }
+ }
+ if (lengths[256] == 0)
+ return -9;
+ err = puff_construct(&lencode, lengths, nlen);
+ if (err && (err < 0 || nlen != lencode.count[0] + lencode.count[1]))
+ return -7;
+ err = puff_construct(&distcode, lengths + nlen, ndist);
+ if (err && (err < 0 || ndist != distcode.count[0] + distcode.count[1]))
+ return -8;
+ return puff_codes(s, &lencode, &distcode);
+}
+static int puff(
+ unsigned char* dest,
+ unsigned long* destlen,
+ const unsigned char* source,
+ unsigned long* sourcelen)
+{
+ struct puff_state s;
+ int last, type;
+ int err;
+ s.out = dest;
+ s.outlen = *destlen;
+ s.outcnt = 0;
+ s.in = source;
+ s.inlen = *sourcelen;
+ s.incnt = 0;
+ s.bitbuf = 0;
+ s.bitcnt = 0;
+ if (setjmp(s.env) != 0)
+ err = 2;
+ else {
+ do {
+ last = puff_bits(&s, 1);
+ type = puff_bits(&s, 2);
+ err = type == 0 ? puff_stored(&s) : (type == 1 ? puff_fixed(&s) : (type == 2 ? puff_dynamic(&s) : -1));
+ if (err != 0)
+ break;
+ } while (!last);
+ }
+ if (err <= 0) {
+ *destlen = s.outcnt;
+ *sourcelen = s.incnt;
+ }
+ return err;
+}
+
+//% END CODE DERIVED FROM puff.{c,h}
+
+#include <errno.h>
+#include <sys/mman.h>
+#define ZLIB_HEADER_WIDTH 2
+
+static int puff_zlib_to_file(
+ const unsigned char* source,
+ unsigned long sourcelen,
+ int dest_fd,
+ unsigned long destlen)
+{
+ if (sourcelen < ZLIB_HEADER_WIDTH) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+ source += ZLIB_HEADER_WIDTH;
+ sourcelen -= ZLIB_HEADER_WIDTH;
+ void* ret = mmap(0, destlen, PROT_WRITE | PROT_READ, MAP_SHARED, dest_fd, 0);
+ if (ret == MAP_FAILED)
+ return -1;
+ unsigned char* dest = (unsigned char*)ret;
+ unsigned long destlen_copy = destlen;
+ int err = puff(dest, &destlen_copy, source, &sourcelen);
+ if (err) {
+ errno = -err;
+ return -1;
+ }
+ return munmap(dest, destlen);
+}
+
+#include <errno.h>
+#include <fcntl.h>
+static long syz_compare_zlib(volatile long data, volatile long size, volatile long zdata, volatile long zsize)
+{
+ int fd = open("./uncompressed", O_RDWR | O_CREAT | O_EXCL, 0666);
+ if (fd == -1)
+ return -1;
+ if (ftruncate(fd, size))
+ return -1;
+ if (puff_zlib_to_file((unsigned char*)zdata, zsize, fd, size))
+ return -1;
+ void* uncompressed = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (uncompressed == MAP_FAILED)
+ return -1;
+ return syz_compare(data, size, (long)uncompressed, size);
+}
+#endif
+
#if SYZ_EXECUTOR || SYZ_SANDBOX_NONE
static void loop();
static int do_sandbox_none(void)
diff --git a/pkg/ipc/ipc_test.go b/pkg/ipc/ipc_test.go
index 0734fbe6e..c1d3af69a 100644
--- a/pkg/ipc/ipc_test.go
+++ b/pkg/ipc/ipc_test.go
@@ -188,3 +188,41 @@ func TestParallel(t *testing.T) {
}
}
}
+
+func TestZlib(t *testing.T) {
+ t.Parallel()
+ target, err := prog.GetTarget(targets.TestOS, targets.TestArch64)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cfg, opts, err := ipcconfig.Default(target)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cfg.Flags |= FlagDebug
+ cfg.Executor = buildExecutor(t, target)
+ defer os.Remove(cfg.Executor)
+ env, err := MakeEnv(cfg, 0)
+ if err != nil {
+ t.Fatalf("failed to create env: %v", err)
+ }
+ defer env.Close()
+ r := rand.New(testutil.RandSource(t))
+ for i := 0; i < 10; i++ {
+ data := testutil.RandMountImage(r)
+ compressed := prog.Compress(data)
+ text := fmt.Sprintf(`syz_compare_zlib(&(0x7f0000000000)="$%s", AUTO, &(0x7f0000800000)="$%s", AUTO)`,
+ prog.EncodeB64(data), prog.EncodeB64(compressed))
+ p, err := target.Deserialize([]byte(text), prog.Strict)
+ if err != nil {
+ t.Fatalf("failed to deserialize empty program: %v", err)
+ }
+ output, info, _, err := env.Exec(opts, p)
+ if err != nil {
+ t.Fatalf("failed to run executor: %v", err)
+ }
+ if info.Calls[0].Errno != 0 {
+ t.Fatalf("data comparison failed: %v\n%s", info.Calls[0].Errno, output)
+ }
+ }
+}
diff --git a/pkg/testutil/testutil.go b/pkg/testutil/testutil.go
index 9a8de26bd..566d728ef 100644
--- a/pkg/testutil/testutil.go
+++ b/pkg/testutil/testutil.go
@@ -22,3 +22,11 @@ func RandSource(t *testing.T) rand.Source {
t.Logf("seed=%v", seed)
return rand.NewSource(seed)
}
+
+func RandMountImage(r *rand.Rand) []byte {
+ const maxLen = 1 << 20 // 1 MB.
+ len := r.Intn(maxLen)
+ slice := make([]byte, len)
+ r.Read(slice)
+ return slice
+}
diff --git a/prog/compression_test.go b/prog/compression_test.go
index 120e5d60f..351d06500 100644
--- a/prog/compression_test.go
+++ b/prog/compression_test.go
@@ -30,7 +30,7 @@ func TestEncode(t *testing.T) {
func testRoundTrip(r *rand.Rand, transform func([]byte) []byte, inverse func([]byte) ([]byte, error)) error {
for i := 0; i < iterCount(); i++ {
- randBytes := randomBytes(r)
+ randBytes := testutil.RandMountImage(r)
resultBytes := transform(randBytes)
resultBytes, err := inverse(resultBytes)
if err != nil {
@@ -42,11 +42,3 @@ func testRoundTrip(r *rand.Rand, transform func([]byte) []byte, inverse func([]b
}
return nil
}
-
-func randomBytes(r *rand.Rand) []byte {
- const maxLen = 1 << 20 // 1 MB.
- len := r.Intn(maxLen)
- slice := make([]byte, len)
- r.Read(slice)
- return slice
-}
diff --git a/sys/test/exec.txt b/sys/test/exec.txt
index 5847afe7f..913251a7d 100644
--- a/sys/test/exec.txt
+++ b/sys/test/exec.txt
@@ -9,6 +9,7 @@ syz_compare(want ptr[in, string], want_len bytesize[want], got ptr[in, compare_d
syz_compare_int$2(n const[2], v0 intptr, v1 intptr)
syz_compare_int$3(n const[3], v0 intptr, v1 intptr, v2 intptr)
syz_compare_int$4(n const[4], v0 intptr, v1 intptr, v2 intptr, v3 intptr)
+syz_compare_zlib(data ptr[in, array[int8]], size bytesize[data], zdata ptr[in, compressed_image], zsize bytesize[zdata]) (no_generate, no_minimize)
compare_data [
align0 align0