dwp_dwarf and dwp_fd are set in libdw_find_split_unit.c:try_dwp_file. This function is called with a CU's split_lock held, but a data race and memory leak can occur if two separate CUs attempt to set dwp_dwarf and dwp_fd concurrently.
Fix this by adding a new Dwarf mutex dwp_lock that protects the lazy loading of dwp_dwarf and dwp_fd. Distinct CUs will now synchronize on the same Dwarf-wide lock when attempting to set dwp_dwarf and dwp_fd. Reads of dwp_dwarf and dwp_fd are not protected by a lock since all reads (besides those in dwarf_end) occur only after the fields are confirmed to have been lazy loaded. This patch adds dwp_lock instead of using the existing dwarf_lock in order to avoid creating conditions for a lock ordering problem where this code path acquires split_lock then dwarf_lock but other code paths might reach this one with dwarf_lock already held. Signed-off-by: Aaron Merey <[email protected]> --- libdw/dwarf_begin_elf.c | 1 + libdw/dwarf_end.c | 1 + libdw/libdwP.h | 3 +++ libdw/libdw_find_split_unit.c | 5 +++++ 4 files changed, 10 insertions(+) diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c index bc236bf9..e157d103 100644 --- a/libdw/dwarf_begin_elf.c +++ b/libdw/dwarf_begin_elf.c @@ -448,6 +448,7 @@ valid_p (Dwarf *result) /* Initialize locks and search_trees. */ mutex_init (result->dwarf_lock); mutex_init (result->macro_lock); + mutex_init (result->dwp_lock); eu_search_tree_init (&result->cu_tree); eu_search_tree_init (&result->tu_tree); eu_search_tree_init (&result->split_tree); diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c index fa27b19e..4a65a35c 100644 --- a/libdw/dwarf_end.c +++ b/libdw/dwarf_end.c @@ -135,6 +135,7 @@ dwarf_end (Dwarf *dwarf) pthread_rwlock_destroy (&dwarf->mem_rwl); mutex_fini (dwarf->dwarf_lock); mutex_fini (dwarf->macro_lock); + mutex_fini (dwarf->dwp_lock); /* Free the pubnames helper structure. */ free (dwarf->pubnames_sets); diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 9e7e2339..3e0c2edc 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -265,6 +265,9 @@ struct Dwarf /* Synchronize access to dwarf_macro_getsrcfiles and cache_op_table. */ mutex_define(, macro_lock); + /* Synchronize lazy-loading of dwp_dwarf and dwp_fd in try_dwp_file. */ + mutex_define(, dwp_lock); + /* Internal memory handling. This is basically a simplified thread-local reimplementation of obstacks. Unfortunately the standard obstack implementation is not usable in libraries. */ diff --git a/libdw/libdw_find_split_unit.c b/libdw/libdw_find_split_unit.c index 3ccf8441..9a20ae25 100644 --- a/libdw/libdw_find_split_unit.c +++ b/libdw/libdw_find_split_unit.c @@ -90,6 +90,8 @@ try_split_file (Dwarf_CU *cu, const char *dwo_path) static void try_dwp_file (Dwarf_CU *cu) { + mutex_lock (cu->dbg->dwp_lock); + if (cu->dbg->dwp_dwarf == NULL) { uint8_t is_dwp; @@ -122,6 +124,7 @@ try_dwp_file (Dwarf_CU *cu) if (dwp_path == NULL) { __libdw_seterrno (DWARF_E_NOMEM); + mutex_unlock (cu->dbg->dwp_lock); return; } memcpy (dwp_path, cu->dbg->elfpath, elfpath_len); @@ -150,6 +153,8 @@ try_dwp_file (Dwarf_CU *cu) cu->dbg->dwp_dwarf = (Dwarf *) -1; } + mutex_unlock (cu->dbg->dwp_lock); + if (cu->dbg->dwp_dwarf != (Dwarf *) -1) { Dwarf_CU *split = __libdw_dwp_findcu_id (cu->dbg->dwp_dwarf, -- 2.55.0
