https://sourceware.org/bugzilla/show_bug.cgi?id=33764

            Bug ID: 33764
           Summary: Plugin inserting only valid linker scripts causes hang
           Product: binutils
           Version: 2.45.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: ld
          Assignee: unassigned at sourceware dot org
          Reporter: jcksn at duck dot com
  Target Milestone: ---

Created attachment 16552
  --> https://sourceware.org/bugzilla/attachment.cgi?id=16552&action=edit
source for a plugin that triggers the bug

If a plugin claims a file using the plugin api then adds an input file which
can be parsed as a valid linker script, ld hangs.

A minimal example to reproduce this behavior is included. Running 'make' should
cause ld to hang.

In my testing, main.c can be any valid c source code and inserted_file can be
any valid linker script; for simplicity in this test case, I've left them both
as empty files as the bug does not depend on their contents

Incorrect behavior:
ld hangs

Expected behavior:
linking finishes as normal

ld versions:
2.38 (distributed with Ubuntu 22.04)
2.45.50.20251207 (built from commit 4afeaca0dd1ce9244311dcc354ed5bc5fff5227e
with gcc 15.2.1, also on Ubuntu 22.04)

patches: none

Suggested cause:
I haven't worked with the code base before, so this could of course be flawed.
My analysis however, after stepping through with a debugger is the following:

Plugins are required to 'capture' some input file (normally, the file still
containing IR that's going to go through LTO). The injected files essentially
replace the captured files during the linking process.

In order to preserve the correct link order though, those injected files should
be in the location of the captured file.

Among other things, ldlang.c->lang_process is responsible for restoring that
correct order. Lang_process calls lang_list_remove_tail to move new data out of
each of file_chain, stat_ptr, and input_file_chain and into a temporary
variable. It then finds the appropriate location in the respective chains and
calls lang_list_insert_after to move the injected files to the correct
location.

If the injected file is an ld script it is not added to the file_chain linked
list. Lang_process does not anticipate this (presumably all current plugins
inject object files). As such, it tries to move 0 entries from the end of
file_chain into the middle of file_chain. That would be fine, except for a bug
in lang_list_remove_tail.

lang_list_remove_tail does not properly handle the case destlist == origlist
(where no entries should end up stored in the original list); origlist->head is
correctly set to NULL, but origlist->tail remains pointing at the tail of the
original list. It should point at origlist->head (since the list is empty).

This causes the subsequent call to lang_list_insert_after to create a cycle in
the linked list, which was the cause of the hang I observed.

Suggested patch:
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 53bbff1f2a6..97247e6c691 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -8123,6 +8123,8 @@ lang_list_remove_tail (lang_statement_list_type
*destlist,
   origlist->tail = destlist->tail;
   destlist->tail = savetail;
   *savetail = NULL;
+  if(origlist->head == NULL)
+    origlist->tail = &origlist->head;
 }

 static lang_statement_union_type **

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to