[binutils-gdb] ld: Issue an error if group nested too deeply

H.J. Lu hjl@sourceware.org
Mon Aug 18 12:17:27 GMT 2025


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=4e46c5a14f39342897cfcb7ecb9c0270f30247f4

commit 4e46c5a14f39342897cfcb7ecb9c0270f30247f4
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Fri Aug 15 06:18:30 2025 -0700

    ld: Issue an error if group nested too deeply
    
    If a linker script has a group nested too deeply by mistake, issue an
    error instead of hanging forever without outputting any error message.
    
            PR ld/33265
            * ldlang.c (MAX_NESTED_GROUP_DEPTH): New.
            (open_input_bfds): Add a pointer argument for the nested group
            count.  Increment the count before the while loop and decrement
            it after the loop.  Issue an error if the nested group count >=
            MAX_NESTED_GROUP_DEPTH when processing input statement.
            (lang_process): Update open_input_bfds calls.
            (cmdline_emit_object_only_section): Likewise.
            * testsuite/ld-scripts/libpr33265-1.a: New file.
            * testsuite/ld-scripts/libpr33265-2.a: Likewise.
            * testsuite/ld-scripts/libpr33265-3a.a: Likewise.
            * testsuite/ld-scripts/libpr33265-3b.a: Likewise.
            * testsuite/ld-scripts/libpr33265-3c.a: Likewise.
            * testsuite/ld-scripts/pr33265-1.d: Likewise.
            * testsuite/ld-scripts/pr33265-2.d: Likewise.
            * testsuite/ld-scripts/pr33265-3.d: Likewise.
            * testsuite/ld-scripts/script.exp: Run PR ld/33265 tests.
    
    Signed-off-by: H.J. Lu <hjl.tools@gmail.com>

Diff:
---
 ld/ldlang.c                             | 41 +++++++++++++++++++++++++--------
 ld/testsuite/ld-scripts/libpr33265-1.a  |  1 +
 ld/testsuite/ld-scripts/libpr33265-2.a  |  1 +
 ld/testsuite/ld-scripts/libpr33265-3a.a |  1 +
 ld/testsuite/ld-scripts/libpr33265-3b.a |  1 +
 ld/testsuite/ld-scripts/libpr33265-3c.a |  1 +
 ld/testsuite/ld-scripts/pr33265-1.d     |  3 +++
 ld/testsuite/ld-scripts/pr33265-2.d     |  3 +++
 ld/testsuite/ld-scripts/pr33265-3.d     |  3 +++
 ld/testsuite/ld-scripts/script.exp      |  4 ++++
 10 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/ld/ldlang.c b/ld/ldlang.c
index fc7a7d2ced6..2e6d0108f98 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -58,6 +58,9 @@
 #define TO_ADDR(X) ((X) >> opb_shift)
 #define TO_SIZE(X) ((X) << opb_shift)
 
+/* The maximum nested group depth.  */
+#define MAX_NESTED_GROUP_DEPTH 100
+
 /* Local variables.  */
 static struct obstack stat_obstack;
 static struct obstack map_obstack;
@@ -3634,18 +3637,21 @@ static struct bfd_link_hash_entry *plugin_undefs = NULL;
 static void
 open_input_bfds (lang_statement_union_type *s,
 		 lang_output_section_statement_type *os,
-		 enum open_bfd_mode mode)
+		 enum open_bfd_mode mode,
+		 unsigned int *nested_group_count_p)
 {
   for (; s != NULL; s = s->header.next)
     {
       switch (s->header.type)
 	{
 	case lang_constructors_statement_enum:
-	  open_input_bfds (constructor_list.head, os, mode);
+	  open_input_bfds (constructor_list.head, os, mode,
+			   nested_group_count_p);
 	  break;
 	case lang_output_section_statement_enum:
 	  os = &s->output_section_statement;
-	  open_input_bfds (os->children.head, os, mode);
+	  open_input_bfds (os->children.head, os, mode,
+			   nested_group_count_p);
 	  break;
 	case lang_wild_statement_enum:
 	  /* Maybe we should load the file's symbols.  */
@@ -3654,7 +3660,8 @@ open_input_bfds (lang_statement_union_type *s,
 	      && !wildcardp (s->wild_statement.filename)
 	      && !archive_path (s->wild_statement.filename))
 	    lookup_name (s->wild_statement.filename);
-	  open_input_bfds (s->wild_statement.children.head, os, mode);
+	  open_input_bfds (s->wild_statement.children.head, os, mode,
+			   nested_group_count_p);
 	  break;
 	case lang_group_statement_enum:
 	  {
@@ -3667,6 +3674,8 @@ open_input_bfds (lang_statement_union_type *s,
 	       until no new symbols are added to the list of undefined
 	       symbols.  */
 
+	    ++*nested_group_count_p;
+
 	    do
 	      {
 #if BFD_SUPPORTS_PLUGINS
@@ -3674,7 +3683,8 @@ open_input_bfds (lang_statement_union_type *s,
 #endif
 		undefs = link_info.hash->undefs_tail;
 		open_input_bfds (s->group_statement.children.head, os,
-				 mode | OPEN_BFD_FORCE);
+				 mode | OPEN_BFD_FORCE,
+				 nested_group_count_p);
 	      }
 	    while (undefs != link_info.hash->undefs_tail
 #if BFD_SUPPORTS_PLUGINS
@@ -3684,6 +3694,8 @@ open_input_bfds (lang_statement_union_type *s,
 		   || (plugin_insert != plugin_insert_save && plugin_undefs)
 #endif
 		   );
+
+	    --*nested_group_count_p;
 	  }
 	  break;
 	case lang_target_statement_enum:
@@ -3696,6 +3708,10 @@ open_input_bfds (lang_statement_union_type *s,
 	      lang_statement_list_type add;
 	      bfd *abfd;
 
+	      if (*nested_group_count_p >= MAX_NESTED_GROUP_DEPTH)
+		fatal (_("%P: group nested too deeply in linker script '%s'\n"),
+		       s->input_statement.filename);
+
 	      s->input_statement.target = current_target;
 
 	      /* If we are being called from within a group, and this
@@ -8285,6 +8301,8 @@ lang_os_merge_sort_children (void)
 void
 lang_process (void)
 {
+  unsigned int nested_group_count = 0;
+
   lang_os_merge_sort_children ();
 
   /* Finalize dynamic list.  */
@@ -8316,7 +8334,8 @@ lang_process (void)
   /* Create a bfd for each input file.  */
   current_target = default_target;
   lang_statement_iteration++;
-  open_input_bfds (statement_list.head, NULL, OPEN_BFD_NORMAL);
+  open_input_bfds (statement_list.head, NULL, OPEN_BFD_NORMAL,
+		   &nested_group_count);
 
   /* Now that open_input_bfds has processed assignments and provide
      statements we can give values to symbolic origin/length now.  */
@@ -8351,7 +8370,8 @@ lang_process (void)
 	last_os = ((lang_output_section_statement_type *)
 		   ((char *) lang_os_list.tail
 		    - offsetof (lang_output_section_statement_type, next)));
-      open_input_bfds (*added.tail, last_os, OPEN_BFD_NORMAL);
+      open_input_bfds (*added.tail, last_os, OPEN_BFD_NORMAL,
+		       &nested_group_count);
       if (plugin_undefs == link_info.hash->undefs_tail)
 	plugin_undefs = NULL;
       /* Restore the global list pointer now they have all been added.  */
@@ -8402,7 +8422,8 @@ lang_process (void)
 	  /* Rescan archives in case new undefined symbols have appeared.  */
 	  files = file_chain;
 	  lang_statement_iteration++;
-	  open_input_bfds (statement_list.head, NULL, OPEN_BFD_RESCAN);
+	  open_input_bfds (statement_list.head, NULL, OPEN_BFD_RESCAN,
+			   &nested_group_count);
 	  lang_list_remove_tail (&file_chain, &files);
 	  while (files.head != NULL)
 	    {
@@ -10886,6 +10907,7 @@ cmdline_emit_object_only_section (void)
   size_t size, off;
   bfd_byte *contents;
   struct stat st;
+  unsigned int nested_group_count = 0;
 
   /* Get a temporary object-only file.  */
   output_filename = make_temp_file (".obj-only.o");
@@ -10922,7 +10944,8 @@ cmdline_emit_object_only_section (void)
   cmdline_get_object_only_input_files ();
 
   /* Open object-only input files.  */
-  open_input_bfds (statement_list.head, NULL, OPEN_BFD_NORMAL);
+  open_input_bfds (statement_list.head, NULL, OPEN_BFD_NORMAL,
+		   &nested_group_count);
 
   ldemul_after_open ();
 
diff --git a/ld/testsuite/ld-scripts/libpr33265-1.a b/ld/testsuite/ld-scripts/libpr33265-1.a
new file mode 100644
index 00000000000..eab1008c0a8
--- /dev/null
+++ b/ld/testsuite/ld-scripts/libpr33265-1.a
@@ -0,0 +1 @@
+GROUP ( libpr33265-1.a )
diff --git a/ld/testsuite/ld-scripts/libpr33265-2.a b/ld/testsuite/ld-scripts/libpr33265-2.a
new file mode 100644
index 00000000000..10f4b91aa9e
--- /dev/null
+++ b/ld/testsuite/ld-scripts/libpr33265-2.a
@@ -0,0 +1 @@
+GROUP ( ./././././/libpr33265-2.a )
diff --git a/ld/testsuite/ld-scripts/libpr33265-3a.a b/ld/testsuite/ld-scripts/libpr33265-3a.a
new file mode 100644
index 00000000000..bdd4f9acb99
--- /dev/null
+++ b/ld/testsuite/ld-scripts/libpr33265-3a.a
@@ -0,0 +1 @@
+GROUP ( ./././././/libpr33265-3b.a )
diff --git a/ld/testsuite/ld-scripts/libpr33265-3b.a b/ld/testsuite/ld-scripts/libpr33265-3b.a
new file mode 100644
index 00000000000..745832899b2
--- /dev/null
+++ b/ld/testsuite/ld-scripts/libpr33265-3b.a
@@ -0,0 +1 @@
+GROUP ( ./././././/libpr33265-3c.a )
diff --git a/ld/testsuite/ld-scripts/libpr33265-3c.a b/ld/testsuite/ld-scripts/libpr33265-3c.a
new file mode 100644
index 00000000000..4583c097bcf
--- /dev/null
+++ b/ld/testsuite/ld-scripts/libpr33265-3c.a
@@ -0,0 +1 @@
+GROUP ( libpr33265-3a.a )
diff --git a/ld/testsuite/ld-scripts/pr33265-1.d b/ld/testsuite/ld-scripts/pr33265-1.d
new file mode 100644
index 00000000000..b0df33d6262
--- /dev/null
+++ b/ld/testsuite/ld-scripts/pr33265-1.d
@@ -0,0 +1,3 @@
+#source: start.s
+#ld: -r --whole-archive -lpr33265-1
+#error: .*group nested too deeply.*
diff --git a/ld/testsuite/ld-scripts/pr33265-2.d b/ld/testsuite/ld-scripts/pr33265-2.d
new file mode 100644
index 00000000000..6e9da740b8b
--- /dev/null
+++ b/ld/testsuite/ld-scripts/pr33265-2.d
@@ -0,0 +1,3 @@
+#source: start.s
+#ld: -r --whole-archive -lpr33265-2
+#error: .*group nested too deeply.*
diff --git a/ld/testsuite/ld-scripts/pr33265-3.d b/ld/testsuite/ld-scripts/pr33265-3.d
new file mode 100644
index 00000000000..a2f42fa0150
--- /dev/null
+++ b/ld/testsuite/ld-scripts/pr33265-3.d
@@ -0,0 +1,3 @@
+#source: start.s
+#ld: -r --whole-archive -lpr33265-3a
+#error: .*group nested too deeply.*
diff --git a/ld/testsuite/ld-scripts/script.exp b/ld/testsuite/ld-scripts/script.exp
index 0b37675ebe8..40bfe1a56e5 100644
--- a/ld/testsuite/ld-scripts/script.exp
+++ b/ld/testsuite/ld-scripts/script.exp
@@ -240,4 +240,8 @@ run_dump_test "segment-start" {{name (default)}}
 run_dump_test "segment-start" {{name (overridden)} \
 			       {ld -Ttext-segment=0x10000000}}
 
+run_dump_test "pr33265-1"
+run_dump_test "pr33265-2"
+run_dump_test "pr33265-3"
+
 set LDFLAGS $old_LDFLAGS


More information about the Binutils-cvs mailing list