Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 1 Jul 1999 12:45:10 +0700 (NSS)
From:      fjoe@iclub.nsu.ru
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   bin/12471: rtld-elf error handling in dlopen is broken
Message-ID:  <199907010545.MAA26360@iclub.nsu.ru>

next in thread | raw e-mail | index | archive | help

>Number:         12471
>Category:       bin
>Synopsis:       rtld-elf error handling in dlopen is broken
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jun 30 22:50:01 PDT 1999
>Closed-Date:
>Last-Modified:
>Originator:     Max Khon
>Release:        FreeBSD 3.2-STABLE i386
>Organization:
Internet Club, Novosibirsk State University
>Environment:

	FreeBSD/i386 3.2-STABLE, Jun 19 12:41:40 NSS 1999
	-current might be affected too.

>Description:

	dlopen returns "valid" dlh when trying to open faulty
	object second time.

>How-To-Repeat:

in the following code `dlopen' returns NULL
on the first iteration (because g() is not defined) -- it's ok
but on the second iteration `dlopen' returns "valid" dlh

--- cut here (Makefile) ---
CFLAGS = -g

all: dl.so main4 

dl.so: dl.c
	gcc -o dl.so $(CFLAGS) -shared -fpic -fPIC dl.c

main4: main4.o
	gcc -o main4 -export-dynamic main4.o

clean:
	rm -f dl.so main4.o
--- cut here ---

--- cut here (dl.c) ---
#include <stdio.h>

void f()
{
	printf("Hello, world!\n");
	g();
}
--- cut here ---

--- cut here (main4.c) ---
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>

main()
{
	void *dlh = NULL;
	void (*f)(void);

	for (;;) {
		getchar();

		if (dlh != NULL) {
			dlclose(dlh);
			dlh = NULL;
		}

		dlh = dlopen("./dl.so", RTLD_NOW);
		if (dlh == NULL) {
			fprintf(stderr, "dlopen: %s\n", dlerror());
			continue;
		}

		f = dlsym(dlh, "f");
		if (f == NULL) {
			fprintf(stderr, "dlsym: %s\n", dlerror());
			dlh = NULL;
			continue;
		}

		f();
	}

	dlclose(dlh);
	return 0;
}
--- cut here ---

>Fix:

	This is a patch for libexec/rtld-elf/rtld.c (-stable).

*** rtld.c.orig	Thu Jul  1 12:00:50 1999
--- rtld.c	Thu Jul  1 12:23:17 1999
*************** static void linkmap_delete(Obj_Entry *);
*** 84,89 ****
--- 84,90 ----
  static int load_needed_objects(Obj_Entry *);
  static int load_preload_objects(void);
  static Obj_Entry *load_object(char *);
+ static void unload_object(Obj_Entry *obj, bool fini_self);
  static Obj_Entry *obj_from_addr(const void *);
  static int relocate_objects(Obj_Entry *, bool);
  static void rtld_exit(void);
*************** load_object(char *path)
*** 967,972 ****
--- 968,1010 ----
      return obj;
  }
  
+ static void unload_object(Obj_Entry *root, bool fini_self)
+ {
+     root->dl_refcount--;
+     unref_object_dag(root);
+     if (root->refcount == 0) {	/* We are finished with some objects. */
+ 	Obj_Entry *obj;
+ 	Obj_Entry **linkp;
+ 
+ 	/* Finalize objects that are about to be unmapped. */
+ 	for (obj = obj_list->next;  obj != NULL;  obj = obj->next) {
+ 	    if (!fini_self && obj == root)
+ 		break;
+ 	    if (obj->refcount == 0 && obj->fini != NULL)
+ 		(*obj->fini)();
+ 	}
+ 
+ 	/* Unmap all objects that are no longer referenced. */
+ 	linkp = &obj_list->next;
+ 	while ((obj = *linkp) != NULL) {
+ 	    if (obj->refcount == 0) {
+ 		munmap(obj->mapbase, obj->mapsize);
+ 		free(obj->path);
+ 		while (obj->needed != NULL) {
+ 		    Needed_Entry *needed = obj->needed;
+ 		    obj->needed = needed->next;
+ 		    free(needed);
+ 		}
+ 		linkmap_delete(obj);
+ 		*linkp = obj->next;
+ 		free(obj);
+ 	    } else
+ 		linkp = &obj->next;
+ 	}
+ 	obj_tail = linkp;
+     }
+ }
+ 
  static Obj_Entry *
  obj_from_addr(const void *addr)
  {
*************** dlclose(void *handle)
*** 1106,1143 ****
  	return -1;
  
      GDB_STATE(RT_DELETE);
! 
!     root->dl_refcount--;
!     unref_object_dag(root);
!     if (root->refcount == 0) {	/* We are finished with some objects. */
! 	Obj_Entry *obj;
! 	Obj_Entry **linkp;
! 
! 	/* Finalize objects that are about to be unmapped. */
! 	for (obj = obj_list->next;  obj != NULL;  obj = obj->next)
! 	    if (obj->refcount == 0 && obj->fini != NULL)
! 		(*obj->fini)();
! 
! 	/* Unmap all objects that are no longer referenced. */
! 	linkp = &obj_list->next;
! 	while ((obj = *linkp) != NULL) {
! 	    if (obj->refcount == 0) {
! 		munmap(obj->mapbase, obj->mapsize);
! 		free(obj->path);
! 		while (obj->needed != NULL) {
! 		    Needed_Entry *needed = obj->needed;
! 		    obj->needed = needed->next;
! 		    free(needed);
! 		}
! 		linkmap_delete(obj);
! 		*linkp = obj->next;
! 		free(obj);
! 	    } else
! 		linkp = &obj->next;
! 	}
! 	obj_tail = linkp;
!     }
! 
      GDB_STATE(RT_CONSISTENT);
  
      return 0;
--- 1144,1150 ----
  	return -1;
  
      GDB_STATE(RT_DELETE);
!     unload_object(root, true);
      GDB_STATE(RT_CONSISTENT);
  
      return 0;
*************** dlopen(const char *name, int mode)
*** 1174,1183 ****
  
  	    /* XXX - Clean up properly after an error. */
  	    if (load_needed_objects(obj) == -1) {
! 		obj->dl_refcount--;
  		obj = NULL;
  	    } else if (relocate_objects(obj, mode == RTLD_NOW) == -1) {
! 		obj->dl_refcount--;
  		obj = NULL;
  	    } else
  		call_init_functions(obj);
--- 1181,1192 ----
  
  	    /* XXX - Clean up properly after an error. */
  	    if (load_needed_objects(obj) == -1) {
! 		GDB_STATE(RT_DELETE);
! 		unload_object(obj, false);
  		obj = NULL;
  	    } else if (relocate_objects(obj, mode == RTLD_NOW) == -1) {
! 		GDB_STATE(RT_DELETE);
! 		unload_object(obj, false);
  		obj = NULL;
  	    } else
  		call_init_functions(obj);

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


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199907010545.MAA26360>