diff options
| -rw-r--r-- | lib/gunzip.c | 10 | ||||
| -rw-r--r-- | test/cmd/unzip.c | 118 |
2 files changed, 126 insertions, 2 deletions
diff --git a/lib/gunzip.c b/lib/gunzip.c index 20cc14f9688..b30cbfc34ef 100644 --- a/lib/gunzip.c +++ b/lib/gunzip.c @@ -246,6 +246,16 @@ int gzwrite(unsigned char *src, size_t len, struct blk_desc *dev, s.next_out = writebuf; } r = inflate(&s, Z_SYNC_FLUSH); + if (r == Z_BUF_ERROR && !s.avail_in && payload_size) { + /* + * The input chunk was exhausted at exactly + * the same time as the write buffer filled + * up, so no progress was possible. This is + * not fatal, let the outer loop refill the + * input chunk. + */ + break; + } if ((r != Z_OK) && (r != Z_STREAM_END)) { printf("Error: inflate() returned %d\n", r); diff --git a/test/cmd/unzip.c b/test/cmd/unzip.c index 623a2785884..e33b6c3fb3a 100644 --- a/test/cmd/unzip.c +++ b/test/cmd/unzip.c @@ -101,11 +101,10 @@ static int dm_test_cmd_zip_unzip(struct unit_test_state *uts) } DM_TEST(dm_test_cmd_zip_unzip, UTF_CONSOLE); -static int dm_test_cmd_zip_gzwrite(struct unit_test_state *uts) +static int bind_mmc9(struct unit_test_state *uts) { struct udevice *dev; ofnode root, node; - int i, j, ret; /* Enable the mmc9 node for this test */ root = oftree_root(oftree_default()); @@ -113,6 +112,15 @@ static int dm_test_cmd_zip_gzwrite(struct unit_test_state *uts) ut_assert(ofnode_valid(node)); ut_assertok(lists_bind_fdt(gd->dm_root, node, &dev, NULL, false)); + return 0; +} + +static int dm_test_cmd_zip_gzwrite(struct unit_test_state *uts) +{ + int i, j, ret; + + ut_assertok(bind_mmc9(uts)); + for (i = 0; i < ARRAY_SIZE(sizes); i++) { ret = do_test_cmd_zip_unzip(uts, sizes[i], true); if (ret) @@ -132,3 +140,109 @@ static int dm_test_cmd_zip_gzwrite(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_cmd_zip_gzwrite, UTF_CONSOLE); + +/* + * Regression test for the case where a decompression input chunk is + * exhausted at exactly the same time as the write buffer fills up, in + * which case gzwrite() used to call inflate() again with no input, + * receive Z_BUF_ERROR back and treat it as a fatal error. + * + * Craft a gzip file by hand from two stored (uncompressed) deflate + * blocks of 1 KiB each, and pick a chunk size that covers exactly the + * 5 byte header plus payload of the first stored block, so that with a + * 1 KiB write buffer the first chunk runs out precisely when the write + * buffer is full. + */ +#define STORED_BLK_HDR_LEN 5 /* deflate stored block header size */ +#define STORED_BLK_LEN SZ_1K /* payload bytes per stored block */ + +static int gzwrite_chunk_boundary(struct unit_test_state *uts) +{ + static const u8 gzip_hdr[10] = { + 0x1f, 0x8b, /* magic */ + 0x08, /* deflate */ + 0x00, /* no flags */ + 0x00, 0x00, 0x00, 0x00, /* mtime */ + 0x00, /* extra flags */ + 0x03, /* OS: unix */ + }; + unsigned long loadaddr = env_get_ulong("loadaddr", 16, 0); + unsigned long decaddr = loadaddr + SZ_1M; + u8 raw[2 * STORED_BLK_LEN]; + const size_t rawsize = sizeof(raw); + unsigned char *gzmap = map_sysmem(loadaddr, sizeof(gzip_hdr) + + 2 * (STORED_BLK_HDR_LEN + + STORED_BLK_LEN) + 8); + unsigned char *decmap = map_sysmem(decaddr, rawsize); + struct blk_desc *mmc_dev_desc; + const u16 len = STORED_BLK_LEN; + const u16 nlen = ~STORED_BLK_LEN & 0xffff; + size_t gzlen, cnt; + u8 *p = gzmap; + u32 crc; + int i; + + ut_assertok(bind_mmc9(uts)); + + for (i = 0; i < rawsize; i++) + raw[i] = (i * 251) & 0xff; + crc = crc32(0, raw, rawsize); + + memcpy(p, gzip_hdr, sizeof(gzip_hdr)); + p += sizeof(gzip_hdr); + for (i = 0; i < 2; i++) { + *p++ = (i == 1) ? 0x01 : 0x00; /* BFINAL on last block */ + *p++ = len & 0xff; /* LEN */ + *p++ = len >> 8; + *p++ = nlen & 0xff; /* NLEN */ + *p++ = nlen >> 8; + memcpy(p, raw + i * STORED_BLK_LEN, STORED_BLK_LEN); + p += STORED_BLK_LEN; + } + *p++ = crc & 0xff; /* CRC32, little endian */ + *p++ = (crc >> 8) & 0xff; + *p++ = (crc >> 16) & 0xff; + *p++ = (crc >> 24) & 0xff; + *p++ = rawsize & 0xff; /* ISIZE, little endian */ + *p++ = (rawsize >> 8) & 0xff; + *p++ = (rawsize >> 16) & 0xff; + *p++ = (rawsize >> 24) & 0xff; + gzlen = p - gzmap; + + ut_assertok(run_commandf("gzwrite mmc 9 %lx %zx %x", loadaddr, + gzlen, STORED_BLK_LEN)); + ut_assert_skip_to_line("\t%zu bytes, crc 0x%08x", rawsize, crc); + + ut_asserteq(9, blk_get_device_by_str("mmc", "9", &mmc_dev_desc)); + cnt = rawsize / mmc_dev_desc->blksz; + ut_assertok(run_commandf("mmc dev 9")); + ut_assert_nextline("switch to partitions #0, OK"); + ut_assert_nextline("mmc9 is current device"); + + ut_assertok(run_commandf("mmc read %lx 0 %zx", decaddr, cnt)); + ut_assert_nextline("MMC read: dev # 9, block # 0, count %zu ... %zu blocks read: OK", + cnt, cnt); + + ut_asserteq_mem(raw, decmap, rawsize); + + ut_assert_console_end(); + + unmap_sysmem(gzmap); + unmap_sysmem(decmap); + + return 0; +} + +static int dm_test_cmd_gzwrite_chunk_boundary(struct unit_test_state *uts) +{ + int ret; + + /* Input chunk: exactly one stored block header plus its payload */ + ut_assertok(env_set_ulong("gzwrite_chunk", + STORED_BLK_HDR_LEN + STORED_BLK_LEN)); + ret = gzwrite_chunk_boundary(uts); + ut_assertok(env_set("gzwrite_chunk", NULL)); + + return ret; +} +DM_TEST(dm_test_cmd_gzwrite_chunk_boundary, UTF_CONSOLE); |
