aboutsummaryrefslogtreecommitdiff
path: root/libctf/testsuite
diff options
context:
space:
mode:
authorNick Alcock <nick.alcock@oracle.com>2023-04-05 17:21:32 +0100
committerNick Alcock <nick.alcock@oracle.com>2023-04-08 16:07:17 +0100
commitd7474051e87c5804c2b674c0d3ad78030f0464a7 (patch)
tree26e0e288d9f536d62807077854efa77a8538cdd6 /libctf/testsuite
parent30a794e9f1db2de9099ed4c494d917d4e86de0fd (diff)
downloadgdb-d7474051e87c5804c2b674c0d3ad78030f0464a7.zip
gdb-d7474051e87c5804c2b674c0d3ad78030f0464a7.tar.gz
gdb-d7474051e87c5804c2b674c0d3ad78030f0464a7.tar.bz2
libctf: propagate errors from parents correctly
CTF dicts have per-dict errno values: as with other errno values these are set on error and left unchanged on success. This means that all errors *must* set the CTF errno: if a call leaves it unchanged, the caller is apt to find a previous, lingering error and misinterpret it as the real error. There are many places in libctf where we carry out operations on parent dicts as a result of carrying out other user-requested operations on child dicts (e.g. looking up information on a pointer to a type will look up the type as well: the pointer might well be in a child and the type it's a pointer to in the parent). Those operations on the parent might fail; if they do, the error must be correctly reflected on the child that the user-visible operation was carried out on. In many places this was not happening. So, audit and fix all those places. Add tests for as many of those cases as possible so they don't regress. libctf/ * ctf-create.c (ctf_add_slice): Use the original dict. * ctf-lookup.c (ctf_lookup_variable): Propagate errors. (ctf_lookup_symbol_idx): Likewise. * ctf-types.c (ctf_member_next): Likewise. (ctf_type_resolve_unsliced): Likewise. (ctf_type_aname): Likewise. (ctf_member_info): Likewise. (ctf_type_rvisit): Likewise. (ctf_func_type_info): Set the error on the right dict. (ctf_type_encoding): Use the original dict. * testsuite/libctf-writable/error-propagation.*: New test.
Diffstat (limited to 'libctf/testsuite')
-rw-r--r--libctf/testsuite/libctf-writable/error-propagation.c164
-rw-r--r--libctf/testsuite/libctf-writable/error-propagation.lk1
2 files changed, 165 insertions, 0 deletions
diff --git a/libctf/testsuite/libctf-writable/error-propagation.c b/libctf/testsuite/libctf-writable/error-propagation.c
new file mode 100644
index 0000000..9d25e63
--- /dev/null
+++ b/libctf/testsuite/libctf-writable/error-propagation.c
@@ -0,0 +1,164 @@
+/* Make sure that errors are propagated properly from parent dicts to children
+ when errors are encountered in child functions that can recurse to parents.
+
+ We check specifically a subset of known-buggy functions.
+ Functions that require a buggy linker to expose, or that only fail on
+ assertion-failure-incurring corrupted dicts, are not tested. */
+
+#include <ctf-api.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char *desc;
+
+static void
+check_prop_err (ctf_dict_t *child, ctf_dict_t *parent, int expected)
+{
+ if (ctf_errno (child) == expected)
+ return;
+
+ if (ctf_errno (parent) == expected)
+ fprintf (stderr, "%s: error propagation failure: error \"%s\" not seen on child, "
+ "but instead on parent\n", desc, ctf_errmsg (ctf_errno (parent)));
+ else
+ fprintf (stderr, "%s: expected error is entirely lost: "
+ "\"%s\" seen on parent, \"%s\" on child\n", desc,
+ ctf_errmsg (ctf_errno (parent)),
+ ctf_errmsg (ctf_errno (child)));
+}
+
+static void
+no_prop_err (void)
+{
+ fprintf (stderr, "%s: expected error return not observed.\n", desc);
+}
+
+int main (void)
+{
+ ctf_dict_t *parent;
+ ctf_dict_t *blank;
+ ctf_dict_t *child;
+ ctf_id_t void_id;
+ ctf_id_t base;
+ ctf_id_t slice;
+ ctf_id_t function;
+ ctf_encoding_t long_encoding = { CTF_INT_SIGNED, 0, sizeof (long) };
+ ctf_encoding_t void_encoding = { CTF_INT_SIGNED, 0, 0 };
+ ctf_encoding_t foo;
+ ctf_funcinfo_t fi;
+ ctf_id_t bar;
+ char *funcname;
+ int err;
+
+ if ((parent = ctf_create (&err)) == NULL
+ || (child = ctf_create (&err)) == NULL
+ || (blank = ctf_create (&err)) == NULL)
+ {
+ fprintf (stderr, "Cannot create dicts: %s\n", ctf_errmsg (err));
+ return 1;
+ }
+
+ if ((ctf_import (child, parent)) < 0)
+ {
+ fprintf (stderr, "cannot import: %s\n", ctf_errmsg (ctf_errno (child)));
+ return 1;
+ }
+
+ if ((void_id = ctf_add_integer (parent, CTF_ADD_ROOT, "void", &void_encoding))
+ == CTF_ERR)
+ goto parent_err;
+
+ if ((base = ctf_add_integer (parent, CTF_ADD_ROOT, "long int", &long_encoding))
+ == CTF_ERR)
+ goto parent_err;
+
+ foo.cte_format = 0;
+ foo.cte_bits = 4;
+ foo.cte_offset = 4;
+ if ((slice = ctf_add_slice (child, CTF_ADD_ROOT, base, &foo)) == CTF_ERR)
+ goto parent_err;
+
+ if (ctf_add_variable (parent, "foo", base) < 0)
+ goto child_err;
+
+ fi.ctc_return = void_id;
+ fi.ctc_argc = 0;
+ fi.ctc_flags = 0;
+ if ((function = ctf_add_function (child, CTF_ADD_ROOT, &fi, NULL)) == CTF_ERR)
+ goto child_err;
+
+ desc = "func info lookup of non-function";
+ if ((ctf_func_type_info (child, base, &fi)) != CTF_ERR)
+ no_prop_err ();
+ check_prop_err (child, parent, ECTF_NOTFUNC);
+
+ desc = "func args lookup of non-function";
+ if ((ctf_func_type_args (child, base, 0, &bar)) != CTF_ERR)
+ no_prop_err ();
+ check_prop_err (child, parent, ECTF_NOTFUNC);
+
+ if ((ctf_import (child, blank)) < 0)
+ {
+ fprintf (stderr, "cannot reimport: %s\n", ctf_errmsg (ctf_errno (child)));
+ return 1;
+ }
+
+ /* This is testing ctf_type_resolve_unsliced(), which is called by the enum
+ functions (which are not themselves buggy). This typea isn't an enum, but
+ that's OK: we're after an error, after all, and the type we're slicing is
+ not visible any longer, so nothing can tell it's not an enum. */
+
+ desc = "child slice resolution";
+ if ((ctf_enum_value (child, slice, "foo", NULL)) != CTF_ERR)
+ no_prop_err ();
+ check_prop_err (child, parent, ECTF_BADID);
+
+ desc = "child slice encoding lookup";
+ if ((ctf_type_encoding (child, slice, &foo)) != CTF_ERR)
+ no_prop_err ();
+ check_prop_err (child, parent, ECTF_BADID);
+
+ desc = "func info lookup of non-function";
+ if ((ctf_func_type_info (child, base, &fi)) != CTF_ERR)
+ no_prop_err ();
+ check_prop_err (child, parent, ECTF_BADID);
+
+ desc = "func args lookup of non-function";
+ if ((ctf_func_type_args (child, base, 0, &bar)) != CTF_ERR)
+ no_prop_err ();
+ check_prop_err (child, parent, ECTF_BADID);
+
+ desc = "child slice addition";
+ if ((slice = ctf_add_slice (child, CTF_ADD_ROOT, base, &foo)) != CTF_ERR)
+ no_prop_err ();
+ check_prop_err (child, parent, ECTF_BADID);
+
+ desc = "variable lookup";
+ if (ctf_lookup_variable (child, "foo") != CTF_ERR)
+ no_prop_err ();
+ check_prop_err (child, parent, ECTF_NOTYPEDAT);
+
+ desc = "function lookup via ctf_type_aname";
+ if ((funcname = ctf_type_aname (child, function)) != NULL)
+ {
+ no_prop_err ();
+ free (funcname);
+ }
+ check_prop_err (child, parent, ECTF_BADID);
+
+ ctf_dict_close (child);
+ ctf_dict_close (parent);
+ ctf_dict_close (blank);
+ fprintf (stderr, "All done.\n");
+ return 0;
+
+ parent_err:
+ fprintf (stderr, "cannot populate parent: %s\n", ctf_errmsg (ctf_errno (parent)));
+ return 1;
+
+ child_err:
+ fprintf (stderr, "cannot populate child: %s\n", ctf_errmsg (ctf_errno (parent)));
+ return 1;
+
+}
diff --git a/libctf/testsuite/libctf-writable/error-propagation.lk b/libctf/testsuite/libctf-writable/error-propagation.lk
new file mode 100644
index 0000000..b944f73
--- /dev/null
+++ b/libctf/testsuite/libctf-writable/error-propagation.lk
@@ -0,0 +1 @@
+All done.