diff options
author | Lancelot SIX <lancelot.six@amd.com> | 2022-07-08 11:37:18 +0100 |
---|---|---|
committer | Lancelot SIX <lsix@lancelotsix.com> | 2022-07-26 08:27:27 +0100 |
commit | bc20e562ec0436b6117b989c0e3d8f66c9d4d979 (patch) | |
tree | 868b81b4f341e2b9cccc7fbc1d8210bb46f93f48 /gdb/value.c | |
parent | 60cd08d40309a2b4ce1daae84074893e6303ad17 (diff) | |
download | gdb-bc20e562ec0436b6117b989c0e3d8f66c9d4d979.zip gdb-bc20e562ec0436b6117b989c0e3d8f66c9d4d979.tar.gz gdb-bc20e562ec0436b6117b989c0e3d8f66c9d4d979.tar.bz2 |
gdb/varobj: Fix use after free in varobj
Varobj object contains references to types, variables (i.e. struct
variable) and expression. All of those can reference data on an
objfile's obstack. It is possible for this objfile to be deleted (and
the obstack to be feed), while the varobj remains valid. Later, if the
user uses the varobj, this will result in a use-after-free error. With
address sanitizer build, this leads to a plain error. For non address
sanitizer build we might see undefined behaviour, which manifest
themself as assertion failures when accessing data backed by feed
memory.
This can be observed if we create a varobj that refers to ta symbol in a
shared library, after either the objfile gets reloaded (using the `file`
command) or after the shared library is unloaded (with a call to dlclose
for example).
This patch fixes those issues by:
- Adding cleanup procedure to the free_objfile observable. When
activated this observer clears expressions referencing the objfile
being freed, and removes references to blocks belonging to this
objfile.
- Adding varobj support in the `preserve_values` (gdb.value.c). This
ensures that before the objfile is unloaded, any type owned by the
objfile referenced by the varobj is replaced by an equivalent type
not owned by the objfile. This process is done here instead of in the
free_objfile observer in order to reuse the type hash table already
used for similar purpose when replacing types of values kept in the
value history.
This patch also makes sure to keep a reference to the expression's
gdbarch and language_defn members when the varobj->root->exp is
initialized. Those structures outlive the objfile, so this is safe.
This is done because those references might be used initialize a python
context even after exp is invalidated. Another approach could have been
to initialize the python context with default gdbarch and language_defn
(i.e. nullptr) if expr is NULL, but since we might still try to display
the value which was obtained by evaluating exp when it was still valid,
keeping track of the context which was used at this time seems
reasonable.
Tested on x86_64-Linux.
Co-Authored-By: Pedro Alves <pedro@palves.net>
Diffstat (limited to 'gdb/value.c')
-rw-r--r-- | gdb/value.c | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/gdb/value.c b/gdb/value.c index c9bec67..d027eef 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -49,6 +49,7 @@ #include "cli/cli-style.h" #include "expop.h" #include "inferior.h" +#include "varobj.h" /* Definition of a user function. */ struct internal_function @@ -2599,6 +2600,25 @@ preserve_one_internalvar (struct internalvar *var, struct objfile *objfile, } } +/* Make sure that all types and values referenced by VAROBJ are updated before + OBJFILE is discarded. COPIED_TYPES is used to prevent cycles and + duplicates. */ + +static void +preserve_one_varobj (struct varobj *varobj, struct objfile *objfile, + htab_t copied_types) +{ + if (varobj->type->is_objfile_owned () + && varobj->type->objfile_owner () == objfile) + { + varobj->type + = copy_type_recursive (objfile, varobj->type, copied_types); + } + + if (varobj->value != nullptr) + preserve_one_value (varobj->value.get (), objfile, copied_types); +} + /* Update the internal variables and value history when OBJFILE is discarded; we must copy the types out of the objfile. New global types will be created for every convenience variable which currently points to @@ -2620,6 +2640,13 @@ preserve_values (struct objfile *objfile) for (var = internalvars; var; var = var->next) preserve_one_internalvar (var, objfile, copied_types.get ()); + /* For the remaining varobj, check that none has type owned by OBJFILE. */ + all_root_varobjs ([&copied_types, objfile] (struct varobj *varobj) + { + preserve_one_varobj (varobj, objfile, + copied_types.get ()); + }); + preserve_ext_lang_values (objfile, copied_types.get ()); } |