kern/133246: dlclose gives segfault when called in the fini function of another module

Michael Moller moller.michael10 at yahoo.com
Tue Mar 31 10:40:02 PDT 2009


>Number:         133246
>Category:       kern
>Synopsis:       dlclose gives segfault when called in the fini function of another module
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Mar 31 17:40:01 UTC 2009
>Closed-Date:
>Last-Modified:
>Originator:     Michael Moller
>Release:        7.1-RELEASE
>Organization:
Sybase, Inc.
>Environment:
FreeBSD xxxx 7.1-RELEASE FreeBSD 7.1-RELEASE #0: Thu Jan  1 14:37:25 UTC 2009     root at logan.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  i386

>Description:
Dlopened ELF module A dlopens ELF module B and keeps a handle to it in a
static C++ object. When the process exits, rtld_exit is called and the fini
function for A is called. This function calls the object static destructor
and it tries to dlclose the handle to B. This gives a segfault, and stack corruption.

>How-To-Repeat:
See attachment for a repro.
The attachment contains 5 files, separated by === cut here ===.
Separate the files, and run "build.sh" to create the shared objects and executable "prog" which will repro the problem.
>Fix:


Patch attached with submission follows:

There are 5 files included below:
build.sh
ctest.h
ctest1.c
ctest2.c
prog.c

To compile, enter:
    ./build.sh
To run the repro, enter:
    ./prog
Result:
    Segmentation fault: 11 (core dumped)

==================== cut here ==================== 
#!/bin/bash

rm *.o
rm *.so
rm prog

g++ -g -Wall -fPIC -c ctest2.c
g++ -g -shared -Wl,-soname,libctest2.so -o libctest2.so ctest2.o
g++ -g -Wall -fPIC -c ctest1.c
g++ -g -shared -Wl,-soname,libctest1.so -o libctest1.so ctest1.o

g++ -g -Wall -I. prog.c -o prog

==================== cut here ==================== 

// *****************************************************
// ctest.h
// *****************************************************

#ifndef CTEST_H
#define CTEST_H

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

void LibInit_1(void);
void LibFini_1(void);
void LibInit_2(void);
void LibFini_2(void);

void ctest1(int *);
void ctest2(int *);

#ifdef __cplusplus
}
#endif

class an_init_fini_execute_me_1 {
    public:
        an_init_fini_execute_me_1 () {
            LibInit_1();
        }
        ~an_init_fini_execute_me_1 () {
            LibFini_1();
        }
};

class an_init_fini_execute_me_2 {
    public:
        an_init_fini_execute_me_2 () {
            LibInit_2();
        }
        ~an_init_fini_execute_me_2 () {
            LibFini_2();
        }
};

#endif

==================== cut here ==================== 

// *****************************************************
// ctest1.c
// *****************************************************

#include <stdlib.h>
#include <dlfcn.h>
#include "ctest.h"

static int init_fini_call_count_1 = 0 ;
static class an_init_fini_execute_me_1 execute_me_1;
static void *lib_handle = NULL;

void LibInit_1( void ) {
    init_fini_call_count_1++ ;
    if( init_fini_call_count_1 == 1 ) {
        fprintf( stderr, "LibInit/Fini - %s of %s\n", "Beg Init", "1" );
        lib_handle = dlopen("libctest2.so", RTLD_LAZY);
        fprintf( stderr, "LibInit/Fini - %s of %s\n", "End Init", "1" );
    }
}

void LibFini_1( void ) {
    init_fini_call_count_1-- ;
    if( init_fini_call_count_1 == 0 )
    {
        fprintf( stderr, "LibInit/Fini - %s of %s\n", "Beg Fini", "1" );
        if( lib_handle != NULL )
        {
            dlclose(lib_handle);
            lib_handle = NULL;
        }
        fprintf( stderr, "LibInit/Fini - %s of %s\n", "End Fini", "1" );
    }
}

void ctest1(int *i)
{
   *i = 1;
}

==================== cut here ==================== 

// *****************************************************
// ctest2.c
// *****************************************************

#include "ctest.h"

static int init_fini_call_count_2 = 0 ;
static class an_init_fini_execute_me_2 execute_me_2;

void LibInit_2( void ) {
    init_fini_call_count_2++ ;
    if( init_fini_call_count_2 == 1 ) {
        fprintf( stderr, "LibInit/Fini - %s of %s\n", "Beg Init", "2" );
        fprintf( stderr, "LibInit/Fini - %s of %s\n", "End Init", "2" );
    }
}

void LibFini_2( void ) {
    init_fini_call_count_2-- ;
    if( init_fini_call_count_2 == 0 )
    {
        fprintf( stderr, "LibInit/Fini - %s of %s\n", "Beg Fini", "2" );
        fprintf( stderr, "LibInit/Fini - %s of %s\n", "End Fini", "2" );
    }
}

void ctest2(int *i)
{
   *i = 2;
}
==================== cut here ==================== 

// *****************************************************
// prog.c
// *****************************************************

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "ctest.h"

int main(int argc, char **argv) 
{
   void *lib_handle;

   lib_handle = dlopen("libctest1.so", RTLD_LAZY);
   if (!lib_handle) 
   {
      fprintf(stderr, "%s\n", dlerror());
      exit(1);
   }

   dlclose(lib_handle);
   return 0;
}



>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list