libbacktrace: support window size in zstd decompression

Reportedly this occurs on AerynOS.

Patch from Ivan Molodetskikh.

	* elf.c (elf_zstd_decompress_frame): Support a compressed block
	that does not set the single segment flag.
	* zstdtest.c (tests): Add tests without single segment.
This commit is contained in:
Ian Lance Taylor
2026-06-27 08:53:22 -07:00
parent 549b81b43b
commit 058d953bb7
2 changed files with 52 additions and 11 deletions
+24 -8
View File
@@ -4364,6 +4364,7 @@ elf_zstd_decompress_frame (const unsigned char **ppin,
uint32_t repeated_offset3;
uint16_t *scratch;
unsigned char hdr;
int single_segment;
int has_checksum;
uint64_t content_size;
int last_block;
@@ -4422,11 +4423,17 @@ elf_zstd_decompress_frame (const unsigned char **ppin,
hdr = *pin++;
/* We expect a single frame. */
if (unlikely ((hdr & (1 << 5)) == 0))
single_segment = (hdr & (1 << 5)) != 0;
if (!single_segment)
{
elf_uncompress_failed ();
return 0;
if (unlikely (pin >= pinend))
{
elf_uncompress_failed ();
return 0;
}
/* We have all the data in memory, so we can ignore the window
size. */
pin++;
}
/* Reserved bit must be zero. */
if (unlikely ((hdr & (1 << 3)) != 0))
@@ -4444,12 +4451,21 @@ elf_zstd_decompress_frame (const unsigned char **ppin,
switch (hdr >> 6)
{
case 0:
if (unlikely (pin >= pinend))
if (!single_segment)
{
elf_uncompress_failed ();
return 0;
/* The content_size is not specified, but we know it from the
section header. */
content_size = (uint64_t) sout;
}
else
{
if (unlikely (pin >= pinend))
{
elf_uncompress_failed ();
return 0;
}
content_size = (uint64_t) *pin++;
}
content_size = (uint64_t) *pin++;
break;
case 1:
if (unlikely (pin + 1 >= pinend))
+28 -3
View File
@@ -182,7 +182,32 @@ static const struct zstd_test tests[] =
"\xb4\x21\x45\x3b\x10\xe4\x7b\x99\x4d\x8a\x36\x64\x5c\x77\x08\x02"
"\xcb\xe0\xce"),
179,
}
},
{
"window-descriptor",
"hello, world\n",
0,
("\x28\xb5\x2f\xfd"
"\x04" /* no Single_Segment_flag; Frame_Content_Size_flag = 0 */
"\x00" /* Window_Descriptor */
"\x69\x00\x00"
"\x68\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x0a"
"\x4c\x1f\xf9\xf1"),
26,
},
{
"window-descriptor-and-content-size",
"hello, world\n",
0,
("\x28\xb5\x2f\xfd"
"\x84" /* no Single_Segment_flag; Frame_Content_Size_flag = 2 */
"\x00" /* Window_Descriptor */
"\x0d\x00\x00\x00" /* Frame_Content_Size */
"\x69\x00\x00"
"\x68\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x0a"
"\x4c\x1f\xf9\xf1"),
30,
},
};
/* Test the hand coded samples. */
@@ -217,7 +242,7 @@ test_samples (struct backtrace_state *state)
error_callback_compress, NULL,
uncompressed, uncompressed_len))
{
fprintf (stderr, "test %s: uncompress failed\n", tests[i].name);
fprintf (stderr, "FAIL: test %s: uncompress failed\n", tests[i].name);
++failures;
}
else
@@ -227,7 +252,7 @@ test_samples (struct backtrace_state *state)
{
size_t j;
fprintf (stderr, "test %s: uncompressed data mismatch\n",
fprintf (stderr, "FAIL: test %s: uncompressed data mismatch\n",
tests[i].name);
for (j = 0; j < uncompressed_len; ++j)
if (tests[i].uncompressed[j] != uncompressed[j])