https://gcc.gnu.org/g:d8b00bf2e1514cd132a9febaa9849ab46cd316f5
commit r16-8102-gd8b00bf2e1514cd132a9febaa9849ab46cd316f5 Author: Christopher Albert <[email protected]> Date: Fri Mar 13 20:50:07 2026 +0100 fortran: Fix use-after-free in CLASS component error recovery [PR124482] The error recovery added in r16-8021 (PR106946) freed CLASS container symbols when removing invalid CLASS components from a derived type. However, gfc_build_class_symbol reuses existing containers when multiple components share the same class type and attributes. Freeing the container for a failed component also invalidated it for previously committed components, causing a use-after-free detectable with valgrind and manifesting as a SEGV on Solaris/SPARC. Fix by deferring CLASS container cleanup until after all failed components are unlinked, then freeing the container only if no remaining component still references it. gcc/fortran/ChangeLog: PR fortran/124482 * decl.cc (gfc_match_data_decl): Defer CLASS container cleanup until after all failed components are unlinked. Check remaining component list before freeing a shared container. Signed-off-by: Christopher Albert <[email protected]> Diff: --- gcc/fortran/decl.cc | 51 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc index 551ce86d4751..d8aa7d1c06f7 100644 --- a/gcc/fortran/decl.cc +++ b/gcc/fortran/decl.cc @@ -6994,7 +6994,13 @@ cleanup: later calls gfc_undo_symbols, the declaration state is rolled back but that helper symbol survives and leaves the component dangling. Ordinary components do not create that extra helper symbol, so leave them in - place for the usual follow-up diagnostics. PR106946. */ + place for the usual follow-up diagnostics. PR106946. + + CLASS containers are shared between components of the same class type + and attributes (gfc_build_class_symbol reuses existing containers). + We must not free a container that is still referenced by a previously + committed component. Unlink and free the components first, then clean + up only orphaned containers. PR124482. */ if (m == MATCH_ERROR && gfc_comp_struct (gfc_current_state ())) { gfc_symbol *block = gfc_current_block (); @@ -7006,27 +7012,52 @@ cleanup: else prev = &block->components; + /* Record the CLASS container from the removed components. + Normally all components in one declaration share a single + container, but per-variable array specs can produce + additional ones; any beyond the first are harmlessly + leaked until namespace destruction. */ + gfc_symbol *fclass_container = NULL; + while (*prev) { gfc_component *c = *prev; if (c->ts.type == BT_CLASS && c->ts.u.derived && c->ts.u.derived->attr.is_class) { - /* Unlink this CLASS component. */ *prev = c->next; - - /* Remove the CLASS container from the namespace. */ - gfc_symbol *fclass = c->ts.u.derived; - if (gfc_find_symtree (fclass->ns->sym_root, fclass->name)) - gfc_delete_symtree (&fclass->ns->sym_root, fclass->name); - gfc_release_symbol (fclass); - - /* Free the component structure. */ + if (!fclass_container) + fclass_container = c->ts.u.derived; + c->ts.u.derived = NULL; gfc_free_component (c); } else prev = &c->next; } + + /* Free the container only if no remaining component still + references it. CLASS containers are shared between + components of the same class type and attributes + (gfc_build_class_symbol reuses existing ones). */ + if (fclass_container) + { + bool shared = false; + for (gfc_component *q = block->components; q; q = q->next) + if (q->ts.type == BT_CLASS + && q->ts.u.derived == fclass_container) + { + shared = true; + break; + } + if (!shared) + { + if (gfc_find_symtree (fclass_container->ns->sym_root, + fclass_container->name)) + gfc_delete_symtree (&fclass_container->ns->sym_root, + fclass_container->name); + gfc_release_symbol (fclass_container); + } + } } }
