aboutsummaryrefslogtreecommitdiff
path: root/libctf/ctf-create.c
diff options
context:
space:
mode:
authorNick Alcock <nick.alcock@oracle.com>2023-10-18 18:34:57 +0100
committerNick Alcock <nick.alcock@oracle.com>2023-10-20 18:09:54 +0100
commitcd4a8fc4f0bc3c4013756404bc06ad1bdddced59 (patch)
tree342f990a6c09a10e4d7906e7bd13b245c4310c38 /libctf/ctf-create.c
parentd605374748fef3d3b1dea713e78bbef9c8b0fb65 (diff)
downloadbinutils-cd4a8fc4f0bc3c4013756404bc06ad1bdddced59.zip
binutils-cd4a8fc4f0bc3c4013756404bc06ad1bdddced59.tar.gz
binutils-cd4a8fc4f0bc3c4013756404bc06ad1bdddced59.tar.bz2
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.
Diffstat (limited to 'libctf/ctf-create.c')
-rw-r--r--libctf/ctf-create.c75
1 files changed, 56 insertions, 19 deletions
diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c
index fff18e5..c83aad3 100644
--- a/libctf/ctf-create.c
+++ b/libctf/ctf-create.c
@@ -299,6 +299,9 @@ ctf_dtd_delete (ctf_dict_t *fp, ctf_dtdef_t *dtd)
ctf_dtdef_t *
ctf_dtd_lookup (const ctf_dict_t *fp, ctf_id_t type)
{
+ if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, type))
+ fp = fp->ctf_parent;
+
return (ctf_dtdef_t *)
ctf_dynhash_lookup (fp->ctf_dthash, (void *) (uintptr_t) type);
}
@@ -712,15 +715,22 @@ ctf_add_array (ctf_dict_t *fp, uint32_t flag, const ctf_arinfo_t *arp)
int
ctf_set_array (ctf_dict_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)
{
+ ctf_dict_t *ofp = fp;
ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, type);
ctf_array_t *vlen;
+ if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, type))
+ fp = fp->ctf_parent;
+
+ if (!(ofp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno (ofp, ECTF_RDONLY));
+
if (!(fp->ctf_flags & LCTF_RDWR))
- return (ctf_set_errno (fp, ECTF_RDONLY));
+ return (ctf_set_errno (ofp, ECTF_RDONLY));
if (dtd == NULL
|| LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info) != CTF_K_ARRAY)
- return (ctf_set_errno (fp, ECTF_BADID));
+ return (ctf_set_errno (ofp, ECTF_BADID));
vlen = (ctf_array_t *) dtd->dtd_vlen;
fp->ctf_flags |= LCTF_DIRTY;
@@ -1040,6 +1050,7 @@ int
ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
int value)
{
+ ctf_dict_t *ofp = fp;
ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, enid);
unsigned char *old_vlen;
ctf_enum_t *en;
@@ -1050,21 +1061,27 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
if (name == NULL)
return (ctf_set_errno (fp, EINVAL));
+ if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, enid))
+ fp = fp->ctf_parent;
+
+ if (!(ofp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno (ofp, ECTF_RDONLY));
+
if (!(fp->ctf_flags & LCTF_RDWR))
- return (ctf_set_errno (fp, ECTF_RDONLY));
+ return (ctf_set_errno (ofp, ECTF_RDONLY));
if (dtd == NULL)
- return (ctf_set_errno (fp, ECTF_BADID));
+ return (ctf_set_errno (ofp, ECTF_BADID));
kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
root = LCTF_INFO_ISROOT (fp, dtd->dtd_data.ctt_info);
vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
if (kind != CTF_K_ENUM)
- return (ctf_set_errno (fp, ECTF_NOTENUM));
+ return (ctf_set_errno (ofp, ECTF_NOTENUM));
if (vlen == CTF_MAX_VLEN)
- return (ctf_set_errno (fp, ECTF_DTFULL));
+ return (ctf_set_errno (ofp, ECTF_DTFULL));
old_vlen = dtd->dtd_vlen;
if (ctf_grow_vlen (fp, dtd, sizeof (ctf_enum_t) * (vlen + 1)) < 0)
@@ -1083,13 +1100,13 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
for (i = 0; i < vlen; i++)
if (strcmp (ctf_strptr (fp, en[i].cte_name), name) == 0)
- return (ctf_set_errno (fp, ECTF_DUPLICATE));
+ return (ctf_set_errno (ofp, ECTF_DUPLICATE));
en[i].cte_name = ctf_str_add_pending (fp, name, &en[i].cte_name);
en[i].cte_value = value;
if (en[i].cte_name == 0 && name != NULL && name[0] != '\0')
- return -1; /* errno is set for us. */
+ return (ctf_set_errno (ofp, ctf_errno (fp)));
dtd->dtd_data.ctt_info = CTF_TYPE_INFO (kind, root, vlen + 1);
@@ -1102,6 +1119,7 @@ int
ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
ctf_id_t type, unsigned long bit_offset)
{
+ ctf_dict_t *ofp = fp;
ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, souid);
ssize_t msize, malign, ssize;
@@ -1111,11 +1129,25 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
unsigned char *old_vlen;
ctf_lmember_t *memb;
+ if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, souid))
+ {
+ /* Adding a child type to a parent, even via the child, is prohibited.
+ Otherwise, climb to the parent and do all work there. */
+
+ if (LCTF_TYPE_ISCHILD (fp, type))
+ return (ctf_set_errno (ofp, ECTF_BADID));
+
+ fp = fp->ctf_parent;
+ }
+
+ if (!(ofp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno (ofp, ECTF_RDONLY));
+
if (!(fp->ctf_flags & LCTF_RDWR))
- return (ctf_set_errno (fp, ECTF_RDONLY));
+ return (ctf_set_errno (ofp, ECTF_RDONLY));
if (dtd == NULL)
- return (ctf_set_errno (fp, ECTF_BADID));
+ return (ctf_set_errno (ofp, ECTF_BADID));
if (name != NULL && name[0] == '\0')
name = NULL;
@@ -1125,14 +1157,14 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
- return (ctf_set_errno (fp, ECTF_NOTSOU));
+ return (ctf_set_errno (ofp, ECTF_NOTSOU));
if (vlen == CTF_MAX_VLEN)
- return (ctf_set_errno (fp, ECTF_DTFULL));
+ return (ctf_set_errno (ofp, ECTF_DTFULL));
old_vlen = dtd->dtd_vlen;
if (ctf_grow_vlen (fp, dtd, sizeof (ctf_lmember_t) * (vlen + 1)) < 0)
- return -1; /* errno is set for us. */
+ return (ctf_set_errno (ofp, ctf_errno (fp)));
memb = (ctf_lmember_t *) dtd->dtd_vlen;
if (dtd->dtd_vlen != old_vlen)
@@ -1149,7 +1181,7 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
{
for (i = 0; i < vlen; i++)
if (strcmp (ctf_strptr (fp, memb[i].ctlm_name), name) == 0)
- return (ctf_set_errno (fp, ECTF_DUPLICATE));
+ return (ctf_set_errno (ofp, ECTF_DUPLICATE));
}
if ((msize = ctf_type_size (fp, type)) < 0 ||
@@ -1200,12 +1232,12 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
if (is_incomplete)
{
- ctf_err_warn (fp, 1, ECTF_INCOMPLETE,
+ ctf_err_warn (ofp, 1, ECTF_INCOMPLETE,
_("ctf_add_member_offset: cannot add member %s of "
"incomplete type %lx to struct %lx without "
"specifying explicit offset\n"),
name ? name : _("(unnamed member)"), type, souid);
- return (ctf_set_errno (fp, ECTF_INCOMPLETE));
+ return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
}
if (ctf_type_encoding (fp, ltype, &linfo) == 0)
@@ -1216,14 +1248,14 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
{
const char *lname = ctf_strraw (fp, memb[vlen - 1].ctlm_name);
- ctf_err_warn (fp, 1, ECTF_INCOMPLETE,
+ ctf_err_warn (ofp, 1, ECTF_INCOMPLETE,
_("ctf_add_member_offset: cannot add member %s of "
"type %lx to struct %lx without specifying "
"explicit offset after member %s of type %lx, "
"which is an incomplete type\n"),
name ? name : _("(unnamed member)"), type, souid,
lname ? lname : _("(unnamed member)"), ltype);
- return -1; /* errno is set for us. */
+ return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
}
/* Round up the offset of the end of the last member to
@@ -1274,9 +1306,14 @@ ctf_add_member_encoded (ctf_dict_t *fp, ctf_id_t souid, const char *name,
const ctf_encoding_t encoding)
{
ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, type);
- int kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
+ int kind;
int otype = type;
+ if (dtd == NULL)
+ return (ctf_set_errno (fp, ECTF_BADID));
+
+ kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
+
if ((kind != CTF_K_INTEGER) && (kind != CTF_K_FLOAT) && (kind != CTF_K_ENUM))
return (ctf_set_errno (fp, ECTF_NOTINTFP));