diff options
author | Mikael Morin <mikael@gcc.gnu.org> | 2023-08-30 14:18:56 +0200 |
---|---|---|
committer | Mikael Morin <mikael@gcc.gnu.org> | 2023-08-30 14:20:05 +0200 |
commit | d58150452976c4ca65ddc811fac78ef956fa96b0 (patch) | |
tree | 416ad179572be1fe7560d2625dbf2428c031fdb9 /gcc/fortran/interface.cc | |
parent | caa7a99a052929d5970677c5b639e1fa5166e334 (diff) | |
download | gcc-d58150452976c4ca65ddc811fac78ef956fa96b0.zip gcc-d58150452976c4ca65ddc811fac78ef956fa96b0.tar.gz gcc-d58150452976c4ca65ddc811fac78ef956fa96b0.tar.bz2 |
fortran: Restore interface to its previous state on error [PR48776]
Keep memory of the content of the current interface body being parsed
and restore it to its previous state if it has been modified at the time
a parse attempt fails.
This fixes memory errors and random segmentation faults caused by
dangling symbol pointers kept in interfaces' linked lists of symbols.
If a parsing attempt fails and symbols are freed, they should also be
removed from the current interface linked list.
As the list of symbol is a linked list, and parsing only adds new
symbols to the head of the list, all that is needed to track the
previous content of the list is a pointer to its previous head.
This adds such a pointer, and the restoration of the list of symbols
to that pointer on error.
PR fortran/48776
gcc/fortran/ChangeLog:
* gfortran.h (gfc_drop_interface_elements_before): New prototype.
(gfc_current_interface_head): Return a reference to the pointer.
* interface.cc (gfc_current_interface_head): Ditto.
(free_interface_elements_until): New function, generalizing
gfc_free_interface.
(gfc_free_interface): Use free_interface_elements_until.
(gfc_drop_interface_elements_before): New function.
* parse.cc
(current_interface_ptr, previous_interface_head): New static variables.
(current_interface_valid_p, get_current_interface_ptr): New functions.
(decode_statement): Initialize previous_interface_head.
(reject_statement): Restore current interface pointer to point to
previous_interface_head.
gcc/testsuite/ChangeLog:
* gfortran.dg/interface_procedure_1.f90: New test.
Diffstat (limited to 'gcc/fortran/interface.cc')
-rw-r--r-- | gcc/fortran/interface.cc | 39 |
1 files changed, 34 insertions, 5 deletions
diff --git a/gcc/fortran/interface.cc b/gcc/fortran/interface.cc index ea82056..c01df04 100644 --- a/gcc/fortran/interface.cc +++ b/gcc/fortran/interface.cc @@ -78,14 +78,17 @@ along with GCC; see the file COPYING3. If not see gfc_interface_info current_interface; -/* Free a singly linked list of gfc_interface structures. */ +/* Free the leading members of the gfc_interface linked list given in INTR + up to the END element (exclusive: the END element is not freed). + If END is not nullptr, it is assumed that END is in the linked list starting + with INTR. */ -void -gfc_free_interface (gfc_interface *intr) +static void +free_interface_elements_until (gfc_interface *intr, gfc_interface *end) { gfc_interface *next; - for (; intr; intr = next) + for (; intr != end; intr = next) { next = intr->next; free (intr); @@ -93,6 +96,32 @@ gfc_free_interface (gfc_interface *intr) } +/* Free a singly linked list of gfc_interface structures. */ + +void +gfc_free_interface (gfc_interface *intr) +{ + free_interface_elements_until (intr, nullptr); +} + + +/* Update the interface pointer given by IFC_PTR to make it point to TAIL. + It is expected that TAIL (if non-null) is in the list pointed to by + IFC_PTR, hence the tail of it. The members of the list before TAIL are + freed before the pointer reassignment. */ + +void +gfc_drop_interface_elements_before (gfc_interface **ifc_ptr, + gfc_interface *tail) +{ + if (ifc_ptr == nullptr) + return; + + free_interface_elements_until (*ifc_ptr, tail); + *ifc_ptr = tail; +} + + /* Change the operators unary plus and minus into binary plus and minus respectively, leaving the rest unchanged. */ @@ -4953,7 +4982,7 @@ gfc_add_interface (gfc_symbol *new_sym) } -gfc_interface * +gfc_interface *& gfc_current_interface_head (void) { switch (current_interface.type) |