diff options
author | Nick Alcock <nick.alcock@oracle.com> | 2021-03-18 12:37:52 +0000 |
---|---|---|
committer | Nick Alcock <nick.alcock@oracle.com> | 2021-03-18 12:40:40 +0000 |
commit | 986e9e3aa03f854bedacef7fac38fe8f009a416c (patch) | |
tree | 7e553bb8e278ab340513d57d67e37a4c2c503109 /libctf/testsuite | |
parent | 2a05d50e90c2c8219dd4119788548f64a934190e (diff) | |
download | binutils-986e9e3aa03f854bedacef7fac38fe8f009a416c.zip binutils-986e9e3aa03f854bedacef7fac38fe8f009a416c.tar.gz binutils-986e9e3aa03f854bedacef7fac38fe8f009a416c.tar.bz2 |
libctf: do not corrupt strings across ctf_serialize
The preceding change revealed a new bug: the string table is sorted for
better compression, so repeated serialization with type (or member)
additions in the middle can move strings around. But every
serialization flushes the set of refs (the memory locations that are
automatically updated with a final string offset when the strtab is
updated), so if we are not to have string offsets go stale, we must do
all ref additions within the serialization code (which walks the
complete set of types and symbols anyway). Unfortunately, we were adding
one ref in another place: the type name in the dynamic type definitions,
which has a ref added to it by ctf_add_generic.
So adding a type, serializing (via, say, one of the ctf_write
functions), adding another type with a name that sorts earlier, and
serializing again will corrupt the name of the first type because it no
longer had a ref pointing to its dtd entry's name when its string offset
was shifted later in the strtab to mae way for the other type.
To ensure that we don't miss strings, we also maintain a set of *pending
refs* that will be added later (during serialization), and remove
entries from that set when the ref is finally added. We always use
ctf_str_add_pending outside ctf-serialize.c, ensure that ctf_serialize
adds all strtab offsets as refs (even those in the dtds) on every
serialization, and mandate that no refs are live on entry to
ctf_serialize and that all pending refs are gone before strtab
finalization. (Of necessity ctf_serialize has to traverse all strtab
offsets in the dtds in order to serialize them, so adding them as refs
at the same time is easy.)
(Note that we still can't erase unused atoms when we roll back, though
we can erase unused refs: members and enums are still not removed by
rollbacks and might reference strings added after the snapshot.)
libctf/ChangeLog
2021-03-18 Nick Alcock <nick.alcock@oracle.com>
* ctf-hash.c (ctf_dynset_elements): New.
* ctf-impl.h (ctf_dynset_elements): Declare it.
(ctf_str_add_pending): Likewise.
(ctf_dict_t) <ctf_str_pending_ref>: New, set of refs that must be
added during serialization.
* ctf-string.c (ctf_str_create_atoms): Initialize it.
(CTF_STR_ADD_REF): New flag.
(CTF_STR_MAKE_PROVISIONAL): Likewise.
(CTF_STR_PENDING_REF): Likewise.
(ctf_str_add_ref_internal): Take a flags word rather than int
params. Populate, and clear out, ctf_str_pending_ref.
(ctf_str_add): Adjust accordingly.
(ctf_str_add_external): Likewise.
(ctf_str_add_pending): New.
(ctf_str_remove_ref): Also remove the potential ref if it is a
pending ref.
* ctf-serialize.c (ctf_serialize): Prohibit addition of strings
with ctf_str_add_ref before serialization. Ensure that the
ctf_str_pending_ref set is empty before strtab finalization.
(ctf_emit_type_sect): Add a ref to the ctt_name.
* ctf-create.c (ctf_add_generic): Add the ctt_name as a pending
ref.
* testsuite/libctf-writable/reserialize-strtab-corruption.*: New test.
Diffstat (limited to 'libctf/testsuite')
-rw-r--r-- | libctf/testsuite/libctf-writable/reserialize-strtab-corruption.c | 91 | ||||
-rw-r--r-- | libctf/testsuite/libctf-writable/reserialize-strtab-corruption.lk | 5 |
2 files changed, 96 insertions, 0 deletions
diff --git a/libctf/testsuite/libctf-writable/reserialize-strtab-corruption.c b/libctf/testsuite/libctf-writable/reserialize-strtab-corruption.c new file mode 100644 index 0000000..1593325 --- /dev/null +++ b/libctf/testsuite/libctf-writable/reserialize-strtab-corruption.c @@ -0,0 +1,91 @@ +/* Make sure serializing a dict (possibly repeatedly) does not corrupt either + type lookup or the string content of the dict. */ + +#include <ctf-api.h> +#include <stdio.h> +#include <stdlib.h> + +int +main (int argc, char *argv[]) +{ + ctf_dict_t *fp; + ctf_id_t zygal, autoschediastic; + ctf_snapshot_id_t snap; + unsigned char *foo; + size_t foo_size; + const char *bar; + int err; + char name[64]; + + /* Adding things after serialization should not corrupt names created before + serialization. */ + + if ((fp = ctf_create (&err)) == NULL) + goto create_err; + + if ((zygal = ctf_add_struct (fp, CTF_ADD_ROOT, "zygal")) == CTF_ERR) + goto add_err; + + if ((foo = ctf_write_mem (fp, &foo_size, 4096)) == NULL) + goto write_err; + free (foo); + + if (ctf_type_name (fp, zygal, name, sizeof (name)) == NULL) + fprintf (stderr, "Can't get name of zygal: %s\n", ctf_errmsg (ctf_errno (fp))); + else + printf ("zygal's name is %s\n", name); + + if ((autoschediastic = ctf_add_enum (fp, CTF_ADD_ROOT, "autoschediastic")) == CTF_ERR) + goto add_err; + + if (ctf_type_name (fp, zygal, name, sizeof (name)) == NULL) + fprintf (stderr, "Can't get name of zygal: %s\n", ctf_errmsg (ctf_errno (fp))); + else + printf ("zygal's name is %s\n", name); + + /* Serializing again should not corrupt names either. */ + if ((foo = ctf_write_mem (fp, &foo_size, 4096)) == NULL) + goto write_err; + free (foo); + + if (ctf_type_name (fp, zygal, name, sizeof (name)) == NULL) + fprintf (stderr, "Can't get name of zygal: %s\n", ctf_errmsg (ctf_errno (fp))); + else + printf ("zygal's name is %s\n", name); + + /* Add another new name, roll back, and make sure the strings are + uncorrupted. */ + + snap = ctf_snapshot (fp); + if (ctf_add_enumerator (fp, autoschediastic, "aichmophobia", 0) < 0) + goto add_err; + + if (ctf_rollback (fp, snap) < 0) + goto roll_err; + + if (ctf_type_name (fp, zygal, name, sizeof (name)) == NULL) + fprintf (stderr, "Can't get name of zygal: %s\n", ctf_errmsg (ctf_errno (fp))); + else + printf ("zygal's name is %s after first rollback\n", name); + + if (ctf_type_name (fp, autoschediastic, name, sizeof (name)) == NULL) + fprintf (stderr, "Can't get name of autoschediastic: %s\n", ctf_errmsg (ctf_errno (fp))); + else + printf ("autoschediastic's name is %s after first rollback\n", name); + + ctf_dict_close (fp); + return 0; + + create_err: + fprintf (stderr, "Cannot create: %s\n", ctf_errmsg (err)); + return 1; + add_err: + fprintf (stderr, "Cannot add: %s\n", ctf_errmsg (ctf_errno (fp))); + return 1; + write_err: + fprintf (stderr, "Cannot serialize: %s\n", ctf_errmsg (ctf_errno (fp))); + return 1; + roll_err: + fprintf (stderr, "Cannot roll back: %s\n", ctf_errmsg (ctf_errno (fp))); + return 1; +} diff --git a/libctf/testsuite/libctf-writable/reserialize-strtab-corruption.lk b/libctf/testsuite/libctf-writable/reserialize-strtab-corruption.lk new file mode 100644 index 0000000..58f7a64 --- /dev/null +++ b/libctf/testsuite/libctf-writable/reserialize-strtab-corruption.lk @@ -0,0 +1,5 @@ +zygal's name is struct zygal +zygal's name is struct zygal +zygal's name is struct zygal +zygal's name is struct zygal after first rollback +autoschediastic's name is enum autoschediastic after first rollback |