Note: This is a beta release of Red Hat Bugzilla 5.0. The data contained within is a snapshot of the live data so any changes you make will not be reflected in the production Bugzilla. Also email is disabled so feel free to test any aspect of the site that you want. File any problems you find or give feedback here.

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: ---

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.