[PATCH][regression][OpenMP][PR120286] Double free detected when using openmp

Jerry D <[email protected]>
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.fortran
Message-ID <[email protected]>
The attached patch fixes this regression. I would like further review on this.
It looks OK and regression tests on x86_64. I also confirmed the segfault when 
running before the patch.

OK for mainline and later backports.

Regards,

Jerry

fortran: Preserve scalar class pointers in OpenMP
  privatization [PR120286]

OpenMP privatization currently treats scalar class pointers like owned
polymorphic class objects.  In the worker cleanup for private/firstprivate
class pointers, the generated code finalizes and frees ptr._data even though
the clause only copied pointer association status from a shared target.

Fix this in gfc_omp_clause_copy_ctor and gfc_omp_clause_dtor by unwrapping
saved descriptors first and by recognizing class-pointer container types
locally in those hooks.  That keeps scalar class pointers on the
association-only path without changing the broader polymorphic mapping
classification used for OpenMP warnings and deep mapping.

Add a runtime regression test for the original private(ptr) crash plus a
firstprivate(ptr) association check.

gcc/fortran/ChangeLog:

	PR fortran/120286
	* trans-openmp.cc (gfc_is_class_pointer_type): New helper.
	(gfc_omp_clause_copy_ctor): Unwrap saved descriptors before
	deciding whether privatization should preserve only pointer
	association.  Handle scalar class pointers on that path too.
	(gfc_omp_clause_dtor): Likewise.

gcc/testsuite/ChangeLog:

	PR fortran/120286
	* gfortran.dg/pr120286.f90: New test.

Signed-off-by: Christopher Albert <[email protected]>
pr120286.diff (text/x-patch, 7.1 KB)
From 985517a4dcc92c4d8996b9530b6e1beb2333b8fc Mon Sep 17 00:00:00 2001
From: Christopher Albert <[email protected]>
Date: Tue, 10 Mar 2026 20:59:48 +0100
Subject: [PATCH] fortran: Preserve scalar class pointers in OpenMP
 privatization [PR120286]

OpenMP privatization currently treats scalar class pointers like owned
polymorphic class objects.  In the worker cleanup for private/firstprivate
class pointers, the generated code finalizes and frees ptr._data even though
the clause only copied pointer association status from a shared target.

Fix this in gfc_omp_clause_copy_ctor and gfc_omp_clause_dtor by unwrapping
saved descriptors first and by recognizing class-pointer container types
locally in those hooks.  That keeps scalar class pointers on the
association-only path without changing the broader polymorphic mapping
classification used for OpenMP warnings and deep mapping.

Add a runtime regression test for the original private(ptr) crash plus a
firstprivate(ptr) association check.

gcc/fortran/ChangeLog:

	PR fortran/120286
	* trans-openmp.cc (gfc_is_class_pointer_type): New helper.
	(gfc_omp_clause_copy_ctor): Unwrap saved descriptors before
	deciding whether privatization should preserve only pointer
	association.  Handle scalar class pointers on that path too.
	(gfc_omp_clause_dtor): Likewise.

gcc/testsuite/ChangeLog:

	PR fortran/120286
	* gfortran.dg/pr120286.f90: New test.

Signed-off-by: Christopher Albert <[email protected]>
---
 gcc/fortran/trans-openmp.cc            | 75 +++++++++++++++++++-------
 gcc/testsuite/gfortran.dg/pr120286.f90 | 50 +++++++++++++++++
 2 files changed, 105 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/pr120286.f90

diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index 3dd4cf272e5..1224df47b26 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -466,8 +466,6 @@ gfc_omp_replace_alloc_by_to_mapping (tree type, tree decl, bool ptr_ok)
 }
 
 
-/* Return true if TYPE is polymorphic but not with pointer attribute.  */
-
 static bool
 gfc_is_polymorphic_nonptr (tree type)
 {
@@ -476,6 +474,30 @@ gfc_is_polymorphic_nonptr (tree type)
   return GFC_CLASS_TYPE_P (type);
 }
 
+/* Return true if TYPE is a class container for a POINTER entity.  */
+
+static bool
+gfc_is_class_pointer_type (tree type)
+{
+  tree name;
+  const char *s;
+
+  if (POINTER_TYPE_P (type))
+    type = TREE_TYPE (type);
+
+  if (!GFC_CLASS_TYPE_P (type))
+    return false;
+
+  name = TYPE_NAME (type);
+  if (name && TREE_CODE (name) == TYPE_DECL)
+    name = DECL_NAME (name);
+  if (!name)
+    return false;
+
+  s = IDENTIFIER_POINTER (name);
+  return startswith (s, "__class_") && s[strlen (s) - 1] == 'p';
+}
+
 /* Return true if TYPE is unlimited polymorphic but not with pointer attribute;
    unlimited means also intrinsic types are handled and _len is used.  */
 
@@ -905,22 +927,29 @@ gfc_omp_clause_copy_ctor (tree clause, tree dest, tree src)
 {
   tree type = TREE_TYPE (dest), ptr, size, call;
   tree decl_type = TREE_TYPE (OMP_CLAUSE_DECL (clause));
+  tree orig_decl = OMP_CLAUSE_DECL (clause);
   tree cond, then_b, else_b;
   stmtblock_t block, cond_block;
 
   gcc_assert (OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_FIRSTPRIVATE
 	      || OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_LINEAR);
 
-  /* Privatize pointer, only; cf. gfc_omp_predetermined_sharing. */
-  if (DECL_P (OMP_CLAUSE_DECL (clause))
-      && GFC_DECL_ASSOCIATE_VAR_P (OMP_CLAUSE_DECL (clause)))
-    return build2 (MODIFY_EXPR, TREE_TYPE (dest), dest, src);
+  if (DECL_ARTIFICIAL (orig_decl)
+      && DECL_LANG_SPECIFIC (orig_decl)
+      && GFC_DECL_SAVED_DESCRIPTOR (orig_decl))
+    {
+      orig_decl = GFC_DECL_SAVED_DESCRIPTOR (orig_decl);
+      decl_type = TREE_TYPE (orig_decl);
+    }
 
-  if (DECL_ARTIFICIAL (OMP_CLAUSE_DECL (clause))
-      && DECL_LANG_SPECIFIC (OMP_CLAUSE_DECL (clause))
-      && GFC_DECL_SAVED_DESCRIPTOR (OMP_CLAUSE_DECL (clause)))
-    decl_type
-      = TREE_TYPE (GFC_DECL_SAVED_DESCRIPTOR (OMP_CLAUSE_DECL (clause)));
+  /* Privatize pointer association only; cf. gfc_omp_predetermined_sharing.
+     This includes scalar class pointers, whose tree type is still the class
+     record even though the Fortran entity has POINTER semantics.  */
+  if (DECL_P (orig_decl)
+      && (GFC_DECL_ASSOCIATE_VAR_P (orig_decl)
+	  || GFC_DECL_GET_SCALAR_POINTER (orig_decl)
+	  || gfc_is_class_pointer_type (decl_type)))
+    return build2 (MODIFY_EXPR, TREE_TYPE (dest), dest, src);
 
   if (gfc_is_polymorphic_nonptr (decl_type))
     {
@@ -1428,17 +1457,23 @@ gfc_omp_clause_dtor (tree clause, tree decl)
 {
   tree type = TREE_TYPE (decl), tem;
   tree decl_type = TREE_TYPE (OMP_CLAUSE_DECL (clause));
+  tree orig_decl = OMP_CLAUSE_DECL (clause);
 
-  /* Only pointer was privatized; cf. gfc_omp_clause_copy_ctor. */
-  if (DECL_P (OMP_CLAUSE_DECL (clause))
-      && GFC_DECL_ASSOCIATE_VAR_P (OMP_CLAUSE_DECL (clause)))
-    return NULL_TREE;
+  if (DECL_ARTIFICIAL (orig_decl)
+      && DECL_LANG_SPECIFIC (orig_decl)
+      && GFC_DECL_SAVED_DESCRIPTOR (orig_decl))
+    {
+      orig_decl = GFC_DECL_SAVED_DESCRIPTOR (orig_decl);
+      decl_type = TREE_TYPE (orig_decl);
+    }
 
-  if (DECL_ARTIFICIAL (OMP_CLAUSE_DECL (clause))
-      && DECL_LANG_SPECIFIC (OMP_CLAUSE_DECL (clause))
-      && GFC_DECL_SAVED_DESCRIPTOR (OMP_CLAUSE_DECL (clause)))
-    decl_type
-	= TREE_TYPE (GFC_DECL_SAVED_DESCRIPTOR (OMP_CLAUSE_DECL (clause)));
+  /* Only pointer association was privatized; cf. gfc_omp_clause_copy_ctor.
+     Scalar class pointers must not finalize or free their targets here.  */
+  if (DECL_P (orig_decl)
+      && (GFC_DECL_ASSOCIATE_VAR_P (orig_decl)
+	  || GFC_DECL_GET_SCALAR_POINTER (orig_decl)
+	  || gfc_is_class_pointer_type (decl_type)))
+    return NULL_TREE;
   if (gfc_is_polymorphic_nonptr (decl_type))
     {
       if (POINTER_TYPE_P (decl_type))
diff --git a/gcc/testsuite/gfortran.dg/pr120286.f90 b/gcc/testsuite/gfortran.dg/pr120286.f90
new file mode 100644
index 00000000000..99c02a78c9c
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/pr120286.f90
@@ -0,0 +1,50 @@
+! { dg-do run }
+! { dg-additional-options "-fopenmp" }
+!
+! PR fortran/120286 - scalar class pointers in OpenMP private/firstprivate
+! clauses must preserve association status without taking ownership.
+
+program main
+  implicit none
+
+  type foo_t
+    integer :: dummy
+  end type foo_t
+
+  type fooPtr_t
+    class(foo_t), pointer :: p
+  end type fooPtr_t
+
+  type fooPtrStack_t
+    class(fooPtr_t), allocatable :: list(:)
+  end type fooPtrStack_t
+
+  type(fooPtrStack_t) :: x
+  class(foo_t), pointer :: ptr
+  integer :: it, n
+  logical :: ok
+
+  allocate (x%list(1))
+  allocate (x%list(1)%p)
+  x%list(1)%p%dummy = 7
+
+  do it = 1, 16
+!$omp parallel do default(none) num_threads(2) private(n, ptr) shared(x)
+    do n = 1, 1
+      ptr => x%list(n)%p
+    end do
+!$omp end parallel do
+  end do
+
+  if (.not. associated (x%list(1)%p)) stop 1
+  if (x%list(1)%p%dummy /= 7) stop 2
+
+  ptr => x%list(1)%p
+  ok = .false.
+
+!$omp parallel default(none) num_threads(1) firstprivate(ptr) shared(x, ok)
+  ok = associated (ptr, x%list(1)%p) .and. ptr%dummy == 7
+!$omp end parallel
+
+  if (.not. ok) stop 3
+end program main
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.