libctf: fix creation-time parent/child dict confusions
authorNick Alcock <nick.alcock@oracle.com>
Wed, 18 Oct 2023 17:34:57 +0000 (18:34 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Fri, 20 Oct 2023 17:09:54 +0000 (18:09 +0100)
commitcd4a8fc4f0bc3c4013756404bc06ad1bdddced59
tree342f990a6c09a10e4d7906e7bd13b245c4310c38
parentd605374748fef3d3b1dea713e78bbef9c8b0fb65
libctf: fix creation-time parent/child dict confusions

The fixes applied a few years ago to resolve confusions between parent and
child dicts at lookup time also apply in various forms to creation.  In
general, if you have a type in a parent dict ctf_imported into a child and
you do something to it, and the parent dict is writable (created via
ctf_create, not opened via ctf_open*) it should work just the same to make
changes to that type via a child dict as it does to make the change
to the parent dict directly -- and nothing you're prohibited from doing
to the parent dict when done directly should be allowed just because
you're doing it via a child.

Specifically, the following don't work when doing things from the child, but
should:

 - adding a member of a type in the parent to a struct or union in the
   parent via ctf_add_member or ctf_add_member_offset: this yields
   ECTF_BADID

 - adding a member of a type in the parent to a struct or union in the
   parent via ctf_add_member_encoded: this dumps core (!).

 - adding an enumerand to an enumerator in the parent: this yields
   ECTF_BADID

 - setting the properties of an array in the parent via ctf_set_array;
   this yields ECTF_BADID

Relatedly, some things work when doing things via a child that should fail,
yielding a CTF dictionary with invalid content (readable, but meaningless):
in particular, you can add a child type to a struct in the parent via
any of the ctf_add_member* family and nothing complains at all, even though
you should never be able to add references to children to parents (since any
given parent can be associated with many different children).

A family of tests is added to check each of these cases independently, since
some can result in coredumps and it would be nice to test the other cases
even if some dump core.  They use a common library to do all the actual
work.  The set of affected API calls was determined by code inspection
(auditing all calls to ctf_dtd_lookup): it's possible that I missed a few,
but I doubt it, since other cases use ctf_lookup* functions, which already
climb to the parent where appropriate.

libctf/ChangeLog:

PR libctf/30985
* ctf-create.c (ctf_dtd_lookup): Traverse to parents if necessary.
(ctf_set_array): Likewise.  Report errors on the child; require
both parent and child to be writable.
(ctf_add_enumerator): Likewise.
(ctf_add_member_offset): Likewise.  Prohibit addition of child types
to structs in the parent.
(ctf_add_member_encoded): Do not dereference a NULL dtd: report
ECTF_BADID instead.
* ctf-string.c (ctf_str_add_ref_internal): Report ENOMEM on the
dict if addition of a string ref fails.
* testsuite/libctf-writable/parent-child-dtd-crash-lib.c: New library.
* testsuite/libctf-writable/parent-child-dtd-enum.*: New test.
* testsuite/libctf-writable/parent-child-dtd-enumerator.*: New test.
* testsuite/libctf-writable/parent-child-dtd-member-encoded.*: New test.
* testsuite/libctf-writable/parent-child-dtd-member-offset.*: New test.
* testsuite/libctf-writable/parent-child-dtd-set-array.*: New test.
* testsuite/libctf-writable/parent-child-dtd-struct.*: New test.
* testsuite/libctf-writable/parent-child-dtd-union.*: New test.
17 files changed:
libctf/ctf-create.c
libctf/ctf-string.c
libctf/testsuite/libctf-writable/parent-child-dtd-crash-lib.c [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-enum.c [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-enum.lk [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-enumerator.c [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-enumerator.lk [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-member-encoded.c [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-member-encoded.lk [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-member-offset.c [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-member-offset.lk [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-set-array.c [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-set-array.lk [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-struct.c [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-struct.lk [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-union.c [new file with mode: 0644]
libctf/testsuite/libctf-writable/parent-child-dtd-union.lk [new file with mode: 0644]