Bug 154641

Summary: dlclose() generates SIGSEGV in destructors of shared libraries
Product: [Fedora] Fedora Reporter: Evgeny Baskakov <ebaskakov>
Component: glibcAssignee: Jakub Jelinek <jakub>
Status: CLOSED RAWHIDE QA Contact: Brian Brock <bbrock>
Severity: high Docs Contact:
Priority: medium    
Version: 3CC: tao
Target Milestone: ---   
Target Release: ---   
Hardware: i686   
OS: Linux   
Whiteboard:
Fixed In Version: 2.3.5-3 Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2005-04-28 12:33:48 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Evgeny Baskakov 2005-04-13 09:45:37 UTC
From Bugzilla Helper:
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6) Gecko/20050317 Firefox/1.0.2

Description of problem:
The attached program causes SIGSEGV on Fedora Core 3 with latest updates. 

The updates were appiled on 4.12.2005 using the "yum update" command.
Most probably the buggy code is in glibc-2.3.5-0.fc3.1.src.rpm.

The program consists of four files:

----- main.c --------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

static void *maindll = NULL;
static void (*entry)() = NULL;

static void check_non_null(void *v, char *msg) {
   if(!v) {
      printf("ERROR: %s\n", msg);
      abort();
   }
}

int main() {
   printf("Program started\n");
   maindll = dlopen("./libmain.so", RTLD_LAZY);
   check_non_null(maindll, dlerror());
   entry = dlsym(maindll, "Entry");
   check_non_null(entry, dlerror());
   printf("Entering the main entry\n");
   entry();
   dlclose(maindll);
   printf("Program ended\n");
   return 0;
}
---------------------------------------------------

----- maindll.c -----------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

static void *child = NULL;

static void check_non_null(void *v, char *msg) {
   if(!v) {
      printf("ERROR: %s\n", msg);
      abort();
   }
}

void __attribute__ ((constructor)) init0() {
   printf("constructor of libmain.so\n");
}


void __attribute__ ((destructor)) fini0() {
   printf("destructor of libmain.so\n");
}


void Entry() {
   printf("[main] Entry of libmain.so started\n");
   child = dlopen("./libchild1.so", RTLD_LAZY);
   check_non_null(child, dlerror());
   printf("[main] Child library loaded\n");
   dlclose(child);
   printf("[main] Child library closed\n");
   printf("[main] Main libmain.so ended\n");
}
---------------------------------------------------

----- child1.c ------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

static void *child = NULL;

static void check_non_null(void *v, char *msg) {
   if(!v) {
      printf("ERROR: %s\n", msg);
      abort();
   }
}

void __attribute__ ((constructor)) init1() {
   printf("constructor of child 1 started\n");
   child = dlopen("./libchild2.so", RTLD_LAZY);
   check_non_null(child, dlerror());
   printf("constructor of child 1 ended\n");
}


void __attribute__ ((destructor)) fini1() {
   printf("destructor of child 1 started\n");
   check_non_null(child, "CHILD IS NULL");
   dlclose(child);
   printf("destructor of child 1 ended\n");
}
---------------------------------------------------

----- child2.c ------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

static void *child = NULL;

static void check_non_null(void *v, char *msg) {
   if(!v) {
      printf("ERROR: %s\n", msg);
      abort();
   }
}

void __attribute__ ((constructor)) init2() {
   printf("constructor of child 2 started\n");
   child = dlopen("./libmain.so", RTLD_LAZY);
   check_non_null(child, dlerror());
   printf("constructor of child 2 ended\n");
}


void __attribute__ ((destructor)) fini2() {
   printf("destructor of child 2 started\n");
   check_non_null(child, "CHILD IS NULL");
   dlclose(child);
   printf("destructor of child 2 ended\n");
}
---------------------------------------------------


Version-Release number of selected component (if applicable):
2.3.5-0.fc3.1

How reproducible:
Always

Steps to Reproduce:
To watch the program's behavior,

0) Save four attached files (main.c, maindll.c, child1.c, child2.c)

1) Compile them:

gcc -g -shared child1.c -o libchild1.so
gcc -g -shared child2.c -o libchild2.so
gcc -g -shared maindll.c -o libmain.so
gcc -g main.c -o main -ldl

3) Run ./main


Actual Results:  The typical output is:

$ ./main
Program started
contructor of libmain.so
Entering the main entry
[main] Entry of libmain.so started
contructor of child 1 started
contructor of child 2 started
contructor of child 2 ended
contructor of child 1 ended
[main] Child library loaded
destructor of child 1 started
destructor of child 2 started
Segmentation fault


Expected Results:  This program works fine on RedHat 8, SuSE 8.0, and Fedora Core 3 without any updates applied. The right output is:

$ ./main
Program started
constructor of libmain.so
Entering the main entry
[main] Entry of libmain.so started
constructor of child 1 started
constructor of child 2 started
constructor of child 2 ended
constructor of child 1 ended
[main] Child library loaded
destructor of child 1 started
destructor of child 2 started
destructor of child 2 ended
destructor of child 1 ended
[main] Child library closed
[main] Main libmain.so ended
destructor of libmain.so
Program ended


Additional info:

Comment 1 Jakub Jelinek 2005-04-28 12:33:48 UTC
Should be fixed in CVS:
http://sources.redhat.com/ml/libc-hacker/2005-04/msg00014.html
and in the current rawhide build.