Bug 11724

Summary: ld.so - Initialization and Termination Functions incorrectly ordered
Product: glibc Reporter: George Gensure <werkt0>
Component: libcAssignee: George Gensure <werkt0>
Status: RESOLVED FIXED    
Severity: normal CC: drepper.fsp, fweimer, glibc-bugs
Priority: P2 Flags: fweimer: security-
Version: 2.11   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:
Attachments: Dependency hierarchy which breaks existing deps sorting (apologies for the complexity)
topological sort implementation for dependency resolution
Revised fix including test case (case fails independently in the previous implementation of ld.so)
Example of post-patch initialization order failure.

Description George Gensure 2010-06-18 18:33:23 UTC
The order of initialization and termination functions called when loading or
exiting via the loader is being sorted incorrectly in particular cases
(described below) according to the TIS ELF 1.2 standard:

"Before the initialization code for any object A is called, the initialization
code for any other objects that object A depends on are called."

The partial sorting algorithm used in elf/dl-deps.c and elf/dl-fini.c is
incorrect when the sorting section is reached with a particular (order
dependent) dependency hierarchy.  I have not been able to boil down to a
canonical example, but have anonymized it, and will attach following submission.
 Additionally, the partial sort is not input-order independent, as multiple runs
of the existing sort on the failure set, repeating if dependencies are found out
of order, is convergent.

The partial sort is not sufficient to perform the dependency analysis on all
inputs given the requirements of the standards doc, and I have drafted a patch
which uses the tsort implementation from coreutils.  This new sorting has been
tested on all cases where the current ld.so initializes things in the incorrect
order, and on a number of other programs.  The inability to reliably initialize
objects in the correct order is a major issue for me, particularly when working
with C++ and globally initialized variables dependent on linked objects'
initializations.
Comment 1 George Gensure 2010-06-18 18:40:16 UTC
Created attachment 4851 [details]
Dependency hierarchy which breaks existing deps sorting (apologies for the complexity)

This is the list of l_initfini in dl-deps.c when the initialization order is
determined which produces incorrect output.  I have left in the leader
dependency for each library which points back to itself.
Comment 2 George Gensure 2010-06-18 20:16:38 UTC
Created attachment 4852 [details]
topological sort implementation for dependency resolution

I've used the implementation of tsort from coreutils here almost verbatim.  One
point of contention might be the (long) cast for comparison in dl-tsort.c - if
there's a better portable pointer equiv integer type that will give a negative
value for subtraction (since pointers won't), that will be fine.  Obviously
this introduces some recursion into ld.so, and I saw in a comment that that is
not desirable.	This implementation works to provide correct init and fini
ordering for everything I've thrown at it.
Comment 3 Ulrich Drepper 2010-06-19 19:17:05 UTC
You'll have to provide a complete small, self-contained test case.
Comment 4 George Gensure 2010-06-21 20:12:17 UTC
Created attachment 4855 [details]
Revised fix including test case (case fails independently in the previous implementation of ld.so)

Test case added to elf/ as tst-initorder, with a comparison test to make sure
that the initialization occurs in order.  This is a failure set for the
previous implementation.

I've also done a couple of tweaks to the existing patch, returning instead of
trying to sort if we run out of memory during fini.  Technically the previous
implementation could be used if memory allocation fails, since it does not use
any new allocations.
Comment 5 Andreas Schwab 2010-06-22 11:28:34 UTC
(In reply to comment #2)
> there's a better portable pointer equiv integer type

int a = (l > p->l) - (l < p->l);
Comment 6 George Gensure 2010-06-22 16:55:15 UTC
(In reply to comment #5)
> int a = (l > p->l) - (l < p->l);

Fine by me.  Do you want an update of the diff for this?
Comment 7 Ulrich Drepper 2011-01-19 20:35:16 UTC
I check in your test and a patch I wrote.
Comment 8 George Gensure 2011-08-02 20:20:58 UTC
Created attachment 5872 [details]
Example of post-patch initialization order failure.

Your patch is not sufficient.  You corrected the given case, but opened up another failure case.  I have attached a reproducer for the failure case.  I recommend again that my previous patch be accepted as it was, since it worked for both of these cases.  The failure condition now, which did not break under the previous implementation, is described as follows:

main => deps1, deps4, deps3
deps1 => deps2
deps2 => deps3
deps3 => deps4
Comment 9 Ulrich Drepper 2011-10-29 17:29:59 UTC
THis has long since been handled.