[to-be-committed v2] PR ld/24600: BFD: Fix use-after-free from `_bfd_load_armap'
"Maciej W. Rozycki" <[email protected]>
| Newsgroups | gmane.comp.gnu.binutils |
|---|---|
| Message-ID | <[email protected]> |
From: Alan Modra <[email protected]> Fix an issue in commit e34fd4bfa6d7 ("PR ld/24600: BFD: Add general linker support for mapless archives") where the symbol map created by `_bfd_load_armap' has its entries discarded by a call to `bfd_release' after return to `_bfd_compute_and_push_armap' where all objalloc memory is freed that came starting from the dummy `first_name' allocation. Restructure `_bfd_compute_and_push_armap' code such that temporary name pointers are kept in local objalloc and always discarded upon exit from the function, while actual name strings are retained according to the `keep_symtab' parameter and copies are not made in `_bfd_load_armap' anymore. This has been found in the context of CVE-2026-19548, <https://nvd.nist.gov/vuln/detail/CVE-2026-19548>. Co-Authored-By: Maciej W. Rozycki <[email protected]> PR ld/24600 * archive.c (_bfd_load_armap): Don't copy name. (_bfd_compute_and_push_armap): Use local objalloc for name pointers. Don't release bfd_alloc'd memory when keep_symdefs. --- Hi, This has passed regression-testing across my usual 263 targets. Given the reduction of the change I've kept the function result casts in this revision. A separate clean-up with no functional change can be done separately. I note that the failure of `push_armap' does not cause the symbol cache to be discarded in the `keep_symdefs' case even though it probably should. I'll offer a separate change for that. Again, I'm going to check this in shortly unless I hear objections. Maciej Changes from v1, <https://inbox.sourceware.org/binutils/[email protected]/>: - Fold in Alan's changes for symbol name strings from <https://inbox.sourceware.org/binutils/[email protected]/>, discarding unrelated clean-up bits. - Also release memory upon `push_armap' failure. --- bfd/archive.c | 48 ++++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 26 deletions(-) binutils-bfd-compute-and-push-armap-objalloc.diff Index: binutils-gdb/bfd/archive.c =================================================================== --- binutils-gdb.orig/bfd/archive.c +++ binutils-gdb/bfd/archive.c @@ -135,6 +135,7 @@ SUBSECTION #include "bfd.h" #include "libiberty.h" #include "libbfd.h" +#include "objalloc.h" #include "aout/ar.h" #include "aout/ranlib.h" #include "safe-ctype.h" @@ -1023,29 +1024,13 @@ _bfd_load_armap (bfd *arch, unsigned int counter < ardata->symdef_count; counter++, set++) { - bfd_size_type namelen = strlen (*map[counter].name) + 1; - char *name = bfd_alloc (arch, namelen); - - if (name == NULL) - { - bfd_set_error (bfd_error_no_memory); - goto release_symdefs; - } - - memcpy (name, *map[counter].name, namelen); - set->name = name; + set->name = *map[counter].name; set->u.abfd = map[counter].abfd; } ardata->symdef_use_bfd = true; arch->has_armap = true; return true; - - release_symdefs: - bfd_release (arch, ardata->symdefs); - ardata->symdef_count = 0; - ardata->symdefs = NULL; - return false; } /* Iterate over members of archive ARCH starting from FIRST_ONE and @@ -2461,6 +2446,7 @@ _bfd_compute_and_push_armap (bfd *arch, unsigned int elength, bool keep_symtab, bool (*push_armap) (bfd *, unsigned int, struct orl *, unsigned int, int)) { + struct objalloc *scratch; char *first_name = NULL; bfd *current; struct orl *map = NULL; @@ -2473,17 +2459,20 @@ _bfd_compute_and_push_armap size_t amt; static bool report_plugin_err = true; + /* We put the symbol names on the arch objalloc and pointers to them + on our scratch objalloc, and then discard them as appropriate. */ + scratch = objalloc_create (); + if (scratch == NULL) + goto objalloc_error_return; + first_name = (char *) bfd_alloc (arch, 1); + if (first_name == NULL) + goto error_return; + amt = orl_max * sizeof (struct orl); map = (struct orl *) bfd_malloc (amt); if (map == NULL) goto error_return; - /* We put the symbol names on the arch objalloc, and then discard - them when done. */ - first_name = (char *) bfd_alloc (arch, 1); - if (first_name == NULL) - goto error_return; - /* Drop all the files called __.SYMDEF, we're going to make our own. */ while (arch->archive_head && strcmp (bfd_get_filename (arch->archive_head), "__.SYMDEF") == 0) @@ -2579,9 +2568,10 @@ _bfd_compute_and_push_armap } namelen = strlen (syms[src_count]->name); amt = sizeof (char *); - map[orl_count].name = (char **) bfd_alloc (arch, amt); + map[orl_count].name = (char **) objalloc_alloc (scratch, + amt); if (map[orl_count].name == NULL) - goto error_return; + goto objalloc_error_return; *(map[orl_count].name) = (char *) bfd_alloc (arch, namelen + 1); if (*(map[orl_count].name) == NULL) @@ -2605,19 +2595,25 @@ _bfd_compute_and_push_armap /* OK, now we have collected all the data, let's push them out. */ ret = push_armap (arch, elength, map, orl_count, stridx); + if (!ret) + goto error_return; free (syms); free (map); - if (first_name != NULL) + if (!keep_symtab) bfd_release (arch, first_name); + objalloc_free (scratch); return ret; + objalloc_error_return: + bfd_set_error (bfd_error_no_memory); error_return: free (syms); free (map); if (first_name != NULL) bfd_release (arch, first_name); + objalloc_free (scratch); return false; }