Skip to content

Commit cd4a8fc

Browse files
committed
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.
1 parent d605374 commit cd4a8fc

17 files changed

+322
-20
lines changed

libctf/ctf-create.c

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ ctf_dtd_delete (ctf_dict_t *fp, ctf_dtdef_t *dtd)
299299
ctf_dtdef_t *
300300
ctf_dtd_lookup (const ctf_dict_t *fp, ctf_id_t type)
301301
{
302+
if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, type))
303+
fp = fp->ctf_parent;
304+
302305
return (ctf_dtdef_t *)
303306
ctf_dynhash_lookup (fp->ctf_dthash, (void *) (uintptr_t) type);
304307
}
@@ -712,15 +715,22 @@ ctf_add_array (ctf_dict_t *fp, uint32_t flag, const ctf_arinfo_t *arp)
712715
int
713716
ctf_set_array (ctf_dict_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)
714717
{
718+
ctf_dict_t *ofp = fp;
715719
ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, type);
716720
ctf_array_t *vlen;
717721

722+
if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, type))
723+
fp = fp->ctf_parent;
724+
725+
if (!(ofp->ctf_flags & LCTF_RDWR))
726+
return (ctf_set_errno (ofp, ECTF_RDONLY));
727+
718728
if (!(fp->ctf_flags & LCTF_RDWR))
719-
return (ctf_set_errno (fp, ECTF_RDONLY));
729+
return (ctf_set_errno (ofp, ECTF_RDONLY));
720730

721731
if (dtd == NULL
722732
|| LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info) != CTF_K_ARRAY)
723-
return (ctf_set_errno (fp, ECTF_BADID));
733+
return (ctf_set_errno (ofp, ECTF_BADID));
724734

725735
vlen = (ctf_array_t *) dtd->dtd_vlen;
726736
fp->ctf_flags |= LCTF_DIRTY;
@@ -1040,6 +1050,7 @@ int
10401050
ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
10411051
int value)
10421052
{
1053+
ctf_dict_t *ofp = fp;
10431054
ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, enid);
10441055
unsigned char *old_vlen;
10451056
ctf_enum_t *en;
@@ -1050,21 +1061,27 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
10501061
if (name == NULL)
10511062
return (ctf_set_errno (fp, EINVAL));
10521063

1064+
if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, enid))
1065+
fp = fp->ctf_parent;
1066+
1067+
if (!(ofp->ctf_flags & LCTF_RDWR))
1068+
return (ctf_set_errno (ofp, ECTF_RDONLY));
1069+
10531070
if (!(fp->ctf_flags & LCTF_RDWR))
1054-
return (ctf_set_errno (fp, ECTF_RDONLY));
1071+
return (ctf_set_errno (ofp, ECTF_RDONLY));
10551072

10561073
if (dtd == NULL)
1057-
return (ctf_set_errno (fp, ECTF_BADID));
1074+
return (ctf_set_errno (ofp, ECTF_BADID));
10581075

10591076
kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
10601077
root = LCTF_INFO_ISROOT (fp, dtd->dtd_data.ctt_info);
10611078
vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
10621079

10631080
if (kind != CTF_K_ENUM)
1064-
return (ctf_set_errno (fp, ECTF_NOTENUM));
1081+
return (ctf_set_errno (ofp, ECTF_NOTENUM));
10651082

10661083
if (vlen == CTF_MAX_VLEN)
1067-
return (ctf_set_errno (fp, ECTF_DTFULL));
1084+
return (ctf_set_errno (ofp, ECTF_DTFULL));
10681085

10691086
old_vlen = dtd->dtd_vlen;
10701087
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,
10831100

10841101
for (i = 0; i < vlen; i++)
10851102
if (strcmp (ctf_strptr (fp, en[i].cte_name), name) == 0)
1086-
return (ctf_set_errno (fp, ECTF_DUPLICATE));
1103+
return (ctf_set_errno (ofp, ECTF_DUPLICATE));
10871104

10881105
en[i].cte_name = ctf_str_add_pending (fp, name, &en[i].cte_name);
10891106
en[i].cte_value = value;
10901107

10911108
if (en[i].cte_name == 0 && name != NULL && name[0] != '\0')
1092-
return -1; /* errno is set for us. */
1109+
return (ctf_set_errno (ofp, ctf_errno (fp)));
10931110

10941111
dtd->dtd_data.ctt_info = CTF_TYPE_INFO (kind, root, vlen + 1);
10951112

@@ -1102,6 +1119,7 @@ int
11021119
ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
11031120
ctf_id_t type, unsigned long bit_offset)
11041121
{
1122+
ctf_dict_t *ofp = fp;
11051123
ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, souid);
11061124

11071125
ssize_t msize, malign, ssize;
@@ -1111,11 +1129,25 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
11111129
unsigned char *old_vlen;
11121130
ctf_lmember_t *memb;
11131131

1132+
if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, souid))
1133+
{
1134+
/* Adding a child type to a parent, even via the child, is prohibited.
1135+
Otherwise, climb to the parent and do all work there. */
1136+
1137+
if (LCTF_TYPE_ISCHILD (fp, type))
1138+
return (ctf_set_errno (ofp, ECTF_BADID));
1139+
1140+
fp = fp->ctf_parent;
1141+
}
1142+
1143+
if (!(ofp->ctf_flags & LCTF_RDWR))
1144+
return (ctf_set_errno (ofp, ECTF_RDONLY));
1145+
11141146
if (!(fp->ctf_flags & LCTF_RDWR))
1115-
return (ctf_set_errno (fp, ECTF_RDONLY));
1147+
return (ctf_set_errno (ofp, ECTF_RDONLY));
11161148

11171149
if (dtd == NULL)
1118-
return (ctf_set_errno (fp, ECTF_BADID));
1150+
return (ctf_set_errno (ofp, ECTF_BADID));
11191151

11201152
if (name != NULL && name[0] == '\0')
11211153
name = NULL;
@@ -1125,14 +1157,14 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
11251157
vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
11261158

11271159
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
1128-
return (ctf_set_errno (fp, ECTF_NOTSOU));
1160+
return (ctf_set_errno (ofp, ECTF_NOTSOU));
11291161

11301162
if (vlen == CTF_MAX_VLEN)
1131-
return (ctf_set_errno (fp, ECTF_DTFULL));
1163+
return (ctf_set_errno (ofp, ECTF_DTFULL));
11321164

11331165
old_vlen = dtd->dtd_vlen;
11341166
if (ctf_grow_vlen (fp, dtd, sizeof (ctf_lmember_t) * (vlen + 1)) < 0)
1135-
return -1; /* errno is set for us. */
1167+
return (ctf_set_errno (ofp, ctf_errno (fp)));
11361168
memb = (ctf_lmember_t *) dtd->dtd_vlen;
11371169

11381170
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,
11491181
{
11501182
for (i = 0; i < vlen; i++)
11511183
if (strcmp (ctf_strptr (fp, memb[i].ctlm_name), name) == 0)
1152-
return (ctf_set_errno (fp, ECTF_DUPLICATE));
1184+
return (ctf_set_errno (ofp, ECTF_DUPLICATE));
11531185
}
11541186

11551187
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,
12001232

12011233
if (is_incomplete)
12021234
{
1203-
ctf_err_warn (fp, 1, ECTF_INCOMPLETE,
1235+
ctf_err_warn (ofp, 1, ECTF_INCOMPLETE,
12041236
_("ctf_add_member_offset: cannot add member %s of "
12051237
"incomplete type %lx to struct %lx without "
12061238
"specifying explicit offset\n"),
12071239
name ? name : _("(unnamed member)"), type, souid);
1208-
return (ctf_set_errno (fp, ECTF_INCOMPLETE));
1240+
return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
12091241
}
12101242

12111243
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,
12161248
{
12171249
const char *lname = ctf_strraw (fp, memb[vlen - 1].ctlm_name);
12181250

1219-
ctf_err_warn (fp, 1, ECTF_INCOMPLETE,
1251+
ctf_err_warn (ofp, 1, ECTF_INCOMPLETE,
12201252
_("ctf_add_member_offset: cannot add member %s of "
12211253
"type %lx to struct %lx without specifying "
12221254
"explicit offset after member %s of type %lx, "
12231255
"which is an incomplete type\n"),
12241256
name ? name : _("(unnamed member)"), type, souid,
12251257
lname ? lname : _("(unnamed member)"), ltype);
1226-
return -1; /* errno is set for us. */
1258+
return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
12271259
}
12281260

12291261
/* 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,
12741306
const ctf_encoding_t encoding)
12751307
{
12761308
ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, type);
1277-
int kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
1309+
int kind;
12781310
int otype = type;
12791311

1312+
if (dtd == NULL)
1313+
return (ctf_set_errno (fp, ECTF_BADID));
1314+
1315+
kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
1316+
12801317
if ((kind != CTF_K_INTEGER) && (kind != CTF_K_FLOAT) && (kind != CTF_K_ENUM))
12811318
return (ctf_set_errno (fp, ECTF_NOTINTFP));
12821319

libctf/ctf-string.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,10 @@ ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str,
170170

171171
if (flags & CTF_STR_ADD_REF)
172172
{
173-
if ((aref = malloc (sizeof (struct ctf_str_atom_ref))) == NULL)
173+
if ((aref = malloc (sizeof (struct ctf_str_atom_ref))) == NULL) {
174+
ctf_set_errno (fp, ENOMEM);
174175
return NULL;
176+
}
175177
aref->caf_ref = ref;
176178
}
177179

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/* Make sure we do various things right that involve DTD lookups of parents
2+
from the perspective of children. */
3+
4+
#include <ctf-api.h>
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <string.h>
8+
9+
enum crash_method { ADD_STRUCT, ADD_UNION, ADD_MEMBER_OFFSET, ADD_MEMBER_ENCODED, ADD_ENUM, ADD_ENUMERATOR, SET_ARRAY };
10+
11+
void
12+
dtd_crash (enum crash_method method, int parent_bigger)
13+
{
14+
ctf_dict_t *pfp, *cfp;
15+
ctf_encoding_t e = { CTF_INT_SIGNED, 0, sizeof (long) };
16+
ctf_id_t ptype, ftype, stype, foo;
17+
int forward_kind = CTF_K_STRUCT;
18+
size_t i;
19+
int err;
20+
21+
/* Maybe make the relevant type IDs in the parent much bigger than those
22+
in the child, or maybe vice versa. */
23+
24+
if ((pfp = ctf_create (&err)) == NULL)
25+
goto create_err;
26+
27+
if (parent_bigger)
28+
{
29+
if ((foo = ctf_add_integer (pfp, CTF_ADD_NONROOT, "blah", &e)) == CTF_ERR)
30+
goto create_parent;
31+
32+
for (i = 0; i < 4096; i++)
33+
if (ctf_add_pointer (pfp, CTF_ADD_NONROOT, foo) == CTF_ERR)
34+
goto create_parent;
35+
}
36+
37+
if ((ptype = ctf_add_integer (pfp, CTF_ADD_NONROOT, "int", &e)) == CTF_ERR)
38+
goto create_parent;
39+
40+
/* Add a forward to a struct, union, or enum (depending on the method) in
41+
the parent, so we can try to replace it in the child and see what
42+
happens. (Most of them are structs, or it doesn't matter, as for
43+
SET_ARRAY; so we do that by default.) */
44+
45+
switch (method)
46+
{
47+
case ADD_UNION:
48+
forward_kind = CTF_K_UNION;
49+
break;
50+
case ADD_ENUM:
51+
case ADD_ENUMERATOR:
52+
forward_kind = CTF_K_ENUM;
53+
break;
54+
/* Placate clang. */
55+
default:
56+
break;
57+
}
58+
59+
if ((ftype = ctf_add_forward (pfp, CTF_ADD_ROOT, "foo", forward_kind)) == CTF_ERR)
60+
goto create_parent;
61+
62+
if ((cfp = ctf_create (&err)) == NULL)
63+
goto create_err;
64+
65+
if (ctf_import (cfp, pfp) < 0)
66+
goto create_child;
67+
68+
if (!parent_bigger)
69+
{
70+
if ((foo = ctf_add_integer (pfp, CTF_ADD_NONROOT, "blah", &e)) == CTF_ERR)
71+
goto create_parent;
72+
73+
for (i = 0; i < 4096; i++)
74+
if (ctf_add_pointer (pfp, CTF_ADD_NONROOT, foo) == CTF_ERR)
75+
goto create_parent;
76+
}
77+
78+
switch (method)
79+
{
80+
/* These try to replace a forward, and should not do so if we're
81+
adding in the child and it's in the parent. */
82+
case ADD_STRUCT:
83+
if ((stype = ctf_add_struct_sized (cfp, CTF_ADD_ROOT, "foo", 1024)) == CTF_ERR)
84+
goto create_child;
85+
if (stype == ftype)
86+
fprintf (stderr, "Forward-promotion spotted!\n");
87+
break;
88+
89+
case ADD_UNION:
90+
if ((stype = ctf_add_union_sized (cfp, CTF_ADD_ROOT, "foo", 1024)) == CTF_ERR)
91+
goto create_child;
92+
if (stype == ftype)
93+
fprintf (stderr, "Forward-promotion spotted!\n");
94+
break;
95+
96+
case ADD_ENUM:
97+
if ((stype = ctf_add_enum (cfp, CTF_ADD_ROOT, "foo")) == CTF_ERR)
98+
goto create_child;
99+
if (stype == ftype)
100+
fprintf (stderr, "Forward-promotion spotted!\n");
101+
break;
102+
103+
/* These try to look up the struct/union/enum we're adding to: make
104+
sure this works from the perspective of the child if the type is in
105+
the parent. Also make sure that addition of child types to parent
106+
types this way is prohibited, and that addition of parent types to
107+
parent types is allowed. */
108+
case ADD_MEMBER_OFFSET:
109+
{
110+
ctf_id_t ctype;
111+
112+
if ((stype = ctf_add_struct (pfp, CTF_ADD_ROOT, "bar")) == CTF_ERR)
113+
goto create_child;
114+
115+
if ((ctype = ctf_add_integer (cfp, CTF_ADD_NONROOT, "xyzzy", &e)) == CTF_ERR)
116+
goto create_child;
117+
118+
if (ctf_add_member_offset (cfp, stype, "member", ptype, 5) == CTF_ERR)
119+
goto create_child;
120+
121+
if (ctf_add_member_offset (cfp, stype, "xyzzy", ctype, 4) != CTF_ERR)
122+
fprintf (stderr, "Addition of child type to parent via child unexpectedly succeeded\n");
123+
else if (ctf_errno (cfp) == 0)
124+
fprintf (stderr, "got error from ctype addition to parent struct, but no error found on child\n");
125+
126+
break;
127+
}
128+
129+
case ADD_ENUMERATOR:
130+
if ((stype = ctf_add_enum (pfp, CTF_ADD_ROOT, "bar")) == CTF_ERR)
131+
goto create_parent;
132+
133+
if (ctf_add_enumerator (cfp, stype, "FOO", 0) == CTF_ERR)
134+
goto create_child;
135+
break;
136+
137+
/* This tries to look up the member type we're adding, and goes wrong
138+
if the struct is in the child and the member type is in the parent. */
139+
case ADD_MEMBER_ENCODED:
140+
if ((stype = ctf_add_struct (cfp, CTF_ADD_ROOT, "foo")) == CTF_ERR)
141+
goto create_child;
142+
143+
if (ctf_add_member_encoded (cfp, stype, "cmember", ptype, 5, e) == CTF_ERR)
144+
goto create_child;
145+
break;
146+
147+
/* This tries to look up the array we're resetting the state of. */
148+
case SET_ARRAY:
149+
{
150+
ctf_arinfo_t ar;
151+
152+
ar.ctr_contents = ptype;
153+
ar.ctr_index = ptype;
154+
ar.ctr_nelems = 5;
155+
156+
if ((stype = ctf_add_array (pfp, CTF_ADD_ROOT, &ar)) == CTF_ERR)
157+
goto create_child;
158+
159+
if (ctf_set_array (cfp, stype, &ar) == CTF_ERR)
160+
goto create_child;
161+
break;
162+
}
163+
}
164+
165+
ctf_dict_close (cfp);
166+
ctf_dict_close (pfp);
167+
168+
return;
169+
170+
create_err:
171+
fprintf (stderr, "Creation failed: %s\n", ctf_errmsg (err));
172+
exit (1);
173+
create_parent:
174+
fprintf (stderr, "Cannot create parent type: %s\n", ctf_errmsg (ctf_errno (pfp)));
175+
exit (1);
176+
create_child:
177+
fprintf (stderr, "Cannot create child type: %s\n", ctf_errmsg (ctf_errno (cfp)));
178+
exit (1);
179+
}

0 commit comments

Comments
 (0)