aboutsummaryrefslogtreecommitdiff
path: root/gcc/fortran/parse.cc
diff options
context:
space:
mode:
authorMikael Morin <mikael@gcc.gnu.org>2023-08-30 14:18:56 +0200
committerMikael Morin <mikael@gcc.gnu.org>2023-08-30 14:20:05 +0200
commitd58150452976c4ca65ddc811fac78ef956fa96b0 (patch)
tree416ad179572be1fe7560d2625dbf2428c031fdb9 /gcc/fortran/parse.cc
parentcaa7a99a052929d5970677c5b639e1fa5166e334 (diff)
downloadgcc-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/parse.cc')
-rw-r--r--gcc/fortran/parse.cc54
1 files changed, 54 insertions, 0 deletions
diff --git a/gcc/fortran/parse.cc b/gcc/fortran/parse.cc
index e797402..8f09ddf 100644
--- a/gcc/fortran/parse.cc
+++ b/gcc/fortran/parse.cc
@@ -37,6 +37,13 @@ gfc_st_label *gfc_statement_label;
static locus label_locus;
static jmp_buf eof_buf;
+/* Respectively pointer and content of the current interface body being parsed
+ as they were at the beginning of decode_statement. Used to restore the
+ interface to its previous state in case a parsed statement is rejected after
+ some symbols have been added to the interface. */
+static gfc_interface **current_interface_ptr = nullptr;
+static gfc_interface *previous_interface_head = nullptr;
+
gfc_state_data *gfc_state_stack;
static bool last_was_use_stmt = false;
bool in_exec_part;
@@ -291,6 +298,46 @@ end_of_block:
return ST_GET_FCN_CHARACTERISTICS;
}
+
+/* Tells whether gfc_get_current_interface_head can be used safely. */
+
+static bool
+current_interface_valid_p ()
+{
+ switch (current_interface.type)
+ {
+ case INTERFACE_INTRINSIC_OP:
+ return current_interface.ns != nullptr;
+
+ case INTERFACE_GENERIC:
+ case INTERFACE_DTIO:
+ return current_interface.sym != nullptr;
+
+ case INTERFACE_USER_OP:
+ return current_interface.uop != nullptr;
+
+ default:
+ return false;
+ }
+}
+
+
+/* Return a pointer to the interface currently being parsed, or nullptr if
+ we are not currently parsing an interface body. */
+
+static gfc_interface **
+get_current_interface_ptr ()
+{
+ if (current_interface_valid_p ())
+ {
+ gfc_interface *& ifc_ptr = gfc_current_interface_head ();
+ return &ifc_ptr;
+ }
+ else
+ return nullptr;
+}
+
+
static bool in_specification_block;
/* This is the primary 'decode_statement'. */
@@ -307,6 +354,11 @@ decode_statement (void)
gfc_clear_error (); /* Clear any pending errors. */
gfc_clear_warning (); /* Clear any pending warnings. */
+ current_interface_ptr = get_current_interface_ptr ();
+ previous_interface_head = current_interface_ptr == nullptr
+ ? nullptr
+ : *current_interface_ptr;
+
gfc_matching_function = false;
if (gfc_match_eos () == MATCH_YES)
@@ -3042,6 +3094,8 @@ reject_statement (void)
{
gfc_free_equiv_until (gfc_current_ns->equiv, gfc_current_ns->old_equiv);
gfc_current_ns->equiv = gfc_current_ns->old_equiv;
+ gfc_drop_interface_elements_before (current_interface_ptr,
+ previous_interface_head);
gfc_reject_data (gfc_current_ns);