ntdll/tests: Add tests for RtlDecompressBuffer.
This commit is contained in:
parent
e3503799d9
commit
0e7bd45571
|
@ -1717,6 +1717,366 @@ static void test_RtlGetCompressionWorkSpaceSize(void)
|
||||||
ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %u\n", decompress_workspace);
|
ok(decompress_workspace == 0x1000, "got wrong decompress_workspace %u\n", decompress_workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* helper for test_RtlDecompressBuffer, checks if a chunk is incomplete */
|
||||||
|
static BOOL is_incomplete_chunk(const UCHAR *compressed, ULONG compressed_size, BOOL check_all)
|
||||||
|
{
|
||||||
|
ULONG chunk_size;
|
||||||
|
|
||||||
|
if (compressed_size <= sizeof(WORD))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
while (compressed_size >= sizeof(WORD))
|
||||||
|
{
|
||||||
|
chunk_size = (*(WORD *)compressed & 0xFFF) + 1;
|
||||||
|
if (compressed_size < sizeof(WORD) + chunk_size)
|
||||||
|
return TRUE;
|
||||||
|
if (!check_all)
|
||||||
|
break;
|
||||||
|
compressed += sizeof(WORD) + chunk_size;
|
||||||
|
compressed_size -= sizeof(WORD) + chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DECOMPRESS_BROKEN_FRAGMENT 1 /* < Win 7 */
|
||||||
|
#define DECOMPRESS_BROKEN_TRUNCATED 2 /* broken on all machines */
|
||||||
|
|
||||||
|
static void test_RtlDecompressBuffer(void)
|
||||||
|
{
|
||||||
|
static const struct
|
||||||
|
{
|
||||||
|
UCHAR compressed[32];
|
||||||
|
ULONG compressed_size;
|
||||||
|
NTSTATUS status;
|
||||||
|
UCHAR uncompressed[32];
|
||||||
|
ULONG uncompressed_size;
|
||||||
|
DWORD broken_flags;
|
||||||
|
}
|
||||||
|
test_lznt[] =
|
||||||
|
{
|
||||||
|
/* 4 byte uncompressed chunk */
|
||||||
|
{
|
||||||
|
{0x03, 0x30, 'W', 'i', 'n', 'e'},
|
||||||
|
6,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"Wine",
|
||||||
|
4,
|
||||||
|
DECOMPRESS_BROKEN_FRAGMENT
|
||||||
|
},
|
||||||
|
/* 8 byte uncompressed chunk */
|
||||||
|
{
|
||||||
|
{0x07, 0x30, 'W', 'i', 'n', 'e', 'W', 'i', 'n', 'e'},
|
||||||
|
10,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"WineWine",
|
||||||
|
8,
|
||||||
|
DECOMPRESS_BROKEN_FRAGMENT
|
||||||
|
},
|
||||||
|
/* 4 byte compressed chunk */
|
||||||
|
{
|
||||||
|
{0x04, 0xB0, 0x00, 'W', 'i', 'n', 'e'},
|
||||||
|
7,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"Wine",
|
||||||
|
4
|
||||||
|
},
|
||||||
|
/* 8 byte compressed chunk */
|
||||||
|
{
|
||||||
|
{0x08, 0xB0, 0x00, 'W', 'i', 'n', 'e', 'W', 'i', 'n', 'e'},
|
||||||
|
11,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"WineWine",
|
||||||
|
8
|
||||||
|
},
|
||||||
|
/* compressed chunk using backwards reference */
|
||||||
|
{
|
||||||
|
{0x06, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x01, 0x30},
|
||||||
|
9,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"WineWine",
|
||||||
|
8,
|
||||||
|
DECOMPRESS_BROKEN_TRUNCATED
|
||||||
|
},
|
||||||
|
/* compressed chunk using backwards reference with length > bytes_read */
|
||||||
|
{
|
||||||
|
{0x06, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x05, 0x30},
|
||||||
|
9,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"WineWineWine",
|
||||||
|
12,
|
||||||
|
DECOMPRESS_BROKEN_TRUNCATED
|
||||||
|
},
|
||||||
|
/* same as above, but unused bits != 0 */
|
||||||
|
{
|
||||||
|
{0x06, 0xB0, 0x30, 'W', 'i', 'n', 'e', 0x01, 0x30},
|
||||||
|
9,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"WineWine",
|
||||||
|
8,
|
||||||
|
DECOMPRESS_BROKEN_TRUNCATED
|
||||||
|
},
|
||||||
|
/* compressed chunk without backwards reference and unused bits != 0 */
|
||||||
|
{
|
||||||
|
{0x01, 0xB0, 0x02, 'W'},
|
||||||
|
4,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"W",
|
||||||
|
1
|
||||||
|
},
|
||||||
|
/* termination sequence after first chunk */
|
||||||
|
{
|
||||||
|
{0x03, 0x30, 'W', 'i', 'n', 'e', 0x00, 0x00, 0x03, 0x30, 'W', 'i', 'n', 'e'},
|
||||||
|
14,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"Wine",
|
||||||
|
4,
|
||||||
|
DECOMPRESS_BROKEN_FRAGMENT
|
||||||
|
},
|
||||||
|
/* compressed chunk using backwards reference with 4 bit offset, 12 bit length */
|
||||||
|
{
|
||||||
|
{0x14, 0xB0, 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||||
|
0x00, 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||||
|
0x01, 0x01, 0xF0},
|
||||||
|
23,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"ABCDEFGHIJKLMNOPABCD",
|
||||||
|
20,
|
||||||
|
DECOMPRESS_BROKEN_TRUNCATED
|
||||||
|
},
|
||||||
|
/* compressed chunk using backwards reference with 5 bit offset, 11 bit length */
|
||||||
|
{
|
||||||
|
{0x15, 0xB0, 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||||
|
0x00, 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||||
|
0x02, 'A', 0x00, 0x78},
|
||||||
|
24,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"ABCDEFGHIJKLMNOPABCD",
|
||||||
|
20,
|
||||||
|
DECOMPRESS_BROKEN_TRUNCATED
|
||||||
|
},
|
||||||
|
/* uncompressed chunk with invalid magic */
|
||||||
|
{
|
||||||
|
{0x03, 0x20, 'W', 'i', 'n', 'e'},
|
||||||
|
6,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"Wine",
|
||||||
|
4,
|
||||||
|
DECOMPRESS_BROKEN_FRAGMENT
|
||||||
|
},
|
||||||
|
/* compressed chunk with invalid magic */
|
||||||
|
{
|
||||||
|
{0x04, 0xA0, 0x00, 'W', 'i', 'n', 'e'},
|
||||||
|
7,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"Wine",
|
||||||
|
4
|
||||||
|
},
|
||||||
|
/* garbage byte after end of buffer */
|
||||||
|
{
|
||||||
|
{0x00, 0xB0, 0x02, 0x01},
|
||||||
|
4,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"",
|
||||||
|
0
|
||||||
|
},
|
||||||
|
/* empty compressed chunk */
|
||||||
|
{
|
||||||
|
{0x00, 0xB0, 0x00},
|
||||||
|
3,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"",
|
||||||
|
0
|
||||||
|
},
|
||||||
|
/* empty compressed chunk with unused bits != 0 */
|
||||||
|
{
|
||||||
|
{0x00, 0xB0, 0x01},
|
||||||
|
3,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
"",
|
||||||
|
0
|
||||||
|
},
|
||||||
|
/* empty input buffer */
|
||||||
|
{
|
||||||
|
{},
|
||||||
|
0,
|
||||||
|
STATUS_BAD_COMPRESSION_BUFFER,
|
||||||
|
},
|
||||||
|
/* incomplete chunk header */
|
||||||
|
{
|
||||||
|
{0x01},
|
||||||
|
1,
|
||||||
|
STATUS_BAD_COMPRESSION_BUFFER
|
||||||
|
},
|
||||||
|
/* incomplete chunk header */
|
||||||
|
{
|
||||||
|
{0x00, 0x30},
|
||||||
|
2,
|
||||||
|
STATUS_BAD_COMPRESSION_BUFFER
|
||||||
|
},
|
||||||
|
/* compressed chunk with invalid backwards reference */
|
||||||
|
{
|
||||||
|
{0x06, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x05, 0x40},
|
||||||
|
9,
|
||||||
|
STATUS_BAD_COMPRESSION_BUFFER
|
||||||
|
},
|
||||||
|
/* compressed chunk with incomplete backwards reference */
|
||||||
|
{
|
||||||
|
{0x05, 0xB0, 0x10, 'W', 'i', 'n', 'e', 0x05},
|
||||||
|
8,
|
||||||
|
STATUS_BAD_COMPRESSION_BUFFER
|
||||||
|
},
|
||||||
|
/* incomplete uncompressed chunk */
|
||||||
|
{
|
||||||
|
{0x07, 0x30, 'W', 'i', 'n', 'e'},
|
||||||
|
6,
|
||||||
|
STATUS_BAD_COMPRESSION_BUFFER
|
||||||
|
},
|
||||||
|
/* incomplete compressed chunk */
|
||||||
|
{
|
||||||
|
{0x08, 0xB0, 0x00, 'W', 'i', 'n', 'e'},
|
||||||
|
7,
|
||||||
|
STATUS_BAD_COMPRESSION_BUFFER
|
||||||
|
},
|
||||||
|
/* two compressed chunks, the second one incomplete */
|
||||||
|
{
|
||||||
|
{0x00, 0xB0, 0x02, 0x00, 0xB0},
|
||||||
|
5,
|
||||||
|
STATUS_BAD_COMPRESSION_BUFFER,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static UCHAR buf[0x2000];
|
||||||
|
NTSTATUS status;
|
||||||
|
ULONG final_size;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!pRtlDecompressBuffer)
|
||||||
|
{
|
||||||
|
win_skip("RtlDecompressBuffer is not available\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test compression format / engine */
|
||||||
|
final_size = 0xdeadbeef;
|
||||||
|
status = pRtlDecompressBuffer(COMPRESSION_FORMAT_NONE, buf, sizeof(buf), test_lznt[0].compressed,
|
||||||
|
test_lznt[0].compressed_size, &final_size);
|
||||||
|
ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status);
|
||||||
|
ok(final_size == 0xdeadbeef, "got wrong final_size %u\n", final_size);
|
||||||
|
|
||||||
|
final_size = 0xdeadbeef;
|
||||||
|
status = pRtlDecompressBuffer(COMPRESSION_FORMAT_DEFAULT, buf, sizeof(buf), test_lznt[0].compressed,
|
||||||
|
test_lznt[0].compressed_size, &final_size);
|
||||||
|
ok(status == STATUS_INVALID_PARAMETER, "got wrong status 0x%08x\n", status);
|
||||||
|
ok(final_size == 0xdeadbeef, "got wrong final_size %u\n", final_size);
|
||||||
|
|
||||||
|
final_size = 0xdeadbeef;
|
||||||
|
status = pRtlDecompressBuffer(0xFF, buf, sizeof(buf), test_lznt[0].compressed,
|
||||||
|
test_lznt[0].compressed_size, &final_size);
|
||||||
|
ok(status == STATUS_UNSUPPORTED_COMPRESSION, "got wrong status 0x%08x\n", status);
|
||||||
|
ok(final_size == 0xdeadbeef, "got wrong final_size %u\n", final_size);
|
||||||
|
|
||||||
|
/* regular tests for RtlDecompressBuffer */
|
||||||
|
for (i = 0; i < sizeof(test_lznt) / sizeof(test_lznt[0]); i++)
|
||||||
|
{
|
||||||
|
trace("Running test %d (compressed_size=%u, uncompressed_size=%u, status=0x%08x)\n",
|
||||||
|
i, test_lznt[i].compressed_size, test_lznt[i].uncompressed_size, test_lznt[i].status);
|
||||||
|
|
||||||
|
/* test with very big buffer */
|
||||||
|
final_size = 0xdeadbeef;
|
||||||
|
memset(buf, 0x11, sizeof(buf));
|
||||||
|
status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, sizeof(buf), test_lznt[i].compressed,
|
||||||
|
test_lznt[i].compressed_size, &final_size);
|
||||||
|
ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER &&
|
||||||
|
(test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT)), "%d: got wrong status 0x%08x\n", i, status);
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
ok(final_size == test_lznt[i].uncompressed_size,
|
||||||
|
"%d: got wrong final_size %u\n", i, final_size);
|
||||||
|
ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size),
|
||||||
|
"%d: got wrong decoded data\n", i);
|
||||||
|
ok(buf[test_lznt[i].uncompressed_size] == 0x11,
|
||||||
|
"%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test that modifier for compression engine is ignored */
|
||||||
|
final_size = 0xdeadbeef;
|
||||||
|
memset(buf, 0x11, sizeof(buf));
|
||||||
|
status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1 | COMPRESSION_ENGINE_MAXIMUM, buf, sizeof(buf),
|
||||||
|
test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size);
|
||||||
|
ok(status == test_lznt[i].status || broken(status == STATUS_BAD_COMPRESSION_BUFFER &&
|
||||||
|
(test_lznt[i].broken_flags & DECOMPRESS_BROKEN_FRAGMENT)), "%d: got wrong status 0x%08x\n", i, status);
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
ok(final_size == test_lznt[i].uncompressed_size,
|
||||||
|
"%d: got wrong final_size %u\n", i, final_size);
|
||||||
|
ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size),
|
||||||
|
"%d: got wrong decoded data\n", i);
|
||||||
|
ok(buf[test_lznt[i].uncompressed_size] == 0x11,
|
||||||
|
"%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test with expected output size */
|
||||||
|
if (test_lznt[i].uncompressed_size > 0)
|
||||||
|
{
|
||||||
|
final_size = 0xdeadbeef;
|
||||||
|
memset(buf, 0x11, sizeof(buf));
|
||||||
|
status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, test_lznt[i].uncompressed_size,
|
||||||
|
test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size);
|
||||||
|
ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status);
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
ok(final_size == test_lznt[i].uncompressed_size,
|
||||||
|
"%d: got wrong final_size %u\n", i, final_size);
|
||||||
|
ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size),
|
||||||
|
"%d: got wrong decoded data\n", i);
|
||||||
|
ok(buf[test_lznt[i].uncompressed_size] == 0x11,
|
||||||
|
"%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test with smaller output size */
|
||||||
|
if (test_lznt[i].uncompressed_size > 1)
|
||||||
|
{
|
||||||
|
final_size = 0xdeadbeef;
|
||||||
|
memset(buf, 0x11, sizeof(buf));
|
||||||
|
status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, test_lznt[i].uncompressed_size - 1,
|
||||||
|
test_lznt[i].compressed, test_lznt[i].compressed_size, &final_size);
|
||||||
|
if (test_lznt[i].broken_flags & DECOMPRESS_BROKEN_TRUNCATED)
|
||||||
|
todo_wine
|
||||||
|
ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08x\n", i, status);
|
||||||
|
else
|
||||||
|
ok(status == test_lznt[i].status, "%d: got wrong status 0x%08x\n", i, status);
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
ok(final_size == test_lznt[i].uncompressed_size - 1,
|
||||||
|
"%d: got wrong final_size %u\n", i, final_size);
|
||||||
|
ok(!memcmp(buf, test_lznt[i].uncompressed, test_lznt[i].uncompressed_size - 1),
|
||||||
|
"%d: got wrong decoded data\n", i);
|
||||||
|
ok(buf[test_lznt[i].uncompressed_size - 1] == 0x11,
|
||||||
|
"%d: buf[%u] was modified\n", i, test_lznt[i].uncompressed_size - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test with zero output size */
|
||||||
|
final_size = 0xdeadbeef;
|
||||||
|
memset(buf, 0x11, sizeof(buf));
|
||||||
|
status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf, 0, test_lznt[i].compressed,
|
||||||
|
test_lznt[i].compressed_size, &final_size);
|
||||||
|
if (is_incomplete_chunk(test_lznt[i].compressed, test_lznt[i].compressed_size, FALSE))
|
||||||
|
ok(status == STATUS_BAD_COMPRESSION_BUFFER, "%d: got wrong status 0x%08x\n", i, status);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ok(status == STATUS_SUCCESS, "%d: got wrong status 0x%08x\n", i, status);
|
||||||
|
ok(final_size == 0, "%d: got wrong final_size %u\n", i, final_size);
|
||||||
|
ok(buf[0] == 0x11, "%d: buf[0] was modified\n", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef DECOMPRESS_BROKEN_FRAGMENT
|
||||||
|
#undef DECOMPRESS_BROKEN_TRUNCATED
|
||||||
|
|
||||||
START_TEST(rtl)
|
START_TEST(rtl)
|
||||||
{
|
{
|
||||||
InitFunctionPtrs();
|
InitFunctionPtrs();
|
||||||
|
@ -1745,4 +2105,5 @@ START_TEST(rtl)
|
||||||
test_LdrLockLoaderLock();
|
test_LdrLockLoaderLock();
|
||||||
test_RtlCompressBuffer();
|
test_RtlCompressBuffer();
|
||||||
test_RtlGetCompressionWorkSpaceSize();
|
test_RtlGetCompressionWorkSpaceSize();
|
||||||
|
test_RtlDecompressBuffer();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue