ctf-archive sanity checks
Nick Alcock
nick.alcock@oracle.com
Mon Nov 3 17:04:06 GMT 2025
On 3 Nov 2025, Alan Modra verbalised:
> Existing code checks that the first uint64_t ctfa_magic field is
> available before reading but neglects to check that the last uint64_t
> ctfa_ctfs is available before reading it in ctf_arc_bufpreamble.
> ctf_arc_bufopen sets up a pointer to the struct ctf_archive in
> ctf_new_archive_internal. Extend the check to cover the entire struct.
Thanks!
I have a more robust fix for this coming in CTFv4, range-checking
absolutely everything (at just the same time as I obsolete creating this
archive format at all), but this seems doable now, with adjustments I'll
be adding shortly.
> Applied.
I'll throw it through my test matrix too :)
> PR 33548
> PR 33549
> * ctf-archive.c (ctf_arc_bufpreamble, ctf_arc_bufopen): Check
> that buffer contains at least an entire struct ctf_archive
> when accessing such a struct.
>
> diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c
> index 6c4595fcc84..63184e66244 100644
> --- a/libctf/ctf-archive.c
> +++ b/libctf/ctf-archive.c
> @@ -395,7 +395,7 @@ const ctf_preamble_t *
> ctf_arc_bufpreamble (const ctf_sect_t *ctfsect)
> {
> if (ctfsect->cts_data != NULL
> - && ctfsect->cts_size > sizeof (uint64_t)
> + && ctfsect->cts_size >= sizeof (struct ctf_archive)
> && (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
> {
> struct ctf_archive *arc = (struct ctf_archive *) ctfsect->cts_data;
I don't think that's quite enough. Right below that, we do
return (const ctf_preamble_t *) ((char *) arc + le64toh (arc->ctfa_ctfs)
+ sizeof (uint64_t));
which is just off the end of struct ctf_archive. So we should check
ctfsect->cts_size >= (sizeof (struct ctf_archive) + sizeof (uint64_t)).
I think. A theoretical (but not malformed!) zero-element archive will
fail this new check where it currently passes and hands back a pointer
to uninitialized garbage. The caller is absolutely not ready to deal
with either this situation or the range-check failing at all, and will
dereference garbage and check it for a flag value.
I'll whip a fix up. (No real point whipping up a test, the code to write
these archives is not long for this world so I'd just have to remove it
again...)
> @@ -418,7 +418,7 @@ ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
> ctf_dict_t *fp = NULL;
>
> if (ctfsect->cts_data != NULL
> - && ctfsect->cts_size > sizeof (uint64_t)
> + && ctfsect->cts_size >= sizeof (struct ctf_archive)
> && (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
> {
> /* The archive is mmappable, so this operation is trivial.
It's reasonable to verify that the archive we return is entirely
in-bounds, so this is fine.
--
NULL && (void)
More information about the Binutils
mailing list