Subject: lib/1763: Dynalinks in 1.1
To: None <gnats-bugs@gnats.netbsd.org>
From: None <etheisen@TECLink.Net>
List: netbsd-bugs
Date: 11/13/1995 22:27:38
>Number:         1763
>Category:       lib
>Synopsis:       RTLD's dlopen() functions in non-standard way
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    lib-bug-people (Library Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Nov 13 23:35:00 1995
>Last-Modified:
>Originator:     Erik M. Theisen
>Organization:
Mtel - Advanced Technologies Center
	
>Release:        25.10.95
>Environment:
	
System: NetBSD ozzy 1.1_ALPHA NetBSD 1.1_ALPHA (OZZY) #0: Wed Oct 25 10:52:00 CDT 1995 root@ozzy:/usr/src/sys/arch/i386/compile/OZZY i386


>Description:
NetBSD's dlopen() call only accepts absolute/relative paths instead of using
the runtime linker's 'hints' bucket to resolve location of DLLs.  This behavior
is not inline with many/most commericial U*IXs.  The exact behavior should be
a call to dlopen, such as dlopen("libname.so", DL_LAZY), and dlopen resolves
the pathname of specified DLL using LD_LIBRARY_PATH env var and/or ldconfig
prepared "hints" bucket.  The use of LD_LIBRARY_PATH is disabled if program is
suid.  If desired, a user can still use absolute/relative paths.

As NetBSD uses revision extensions on it's DLL names, a call, such as
dlopen("libname.so.xx.xx", DL_LAZY) where xx are the major and minor revisions
of library, should also work.

>How-To-Repeat:
Call dlopen with a 'shortened' name such as "libc.so" ensuring that libc.so is
not in the current directory.  The call should fail.  dlerror() reports
"No such file or directory".

>Fix:
The following patch modifies the behavior of dlopen so that dlopen calls, with
pathnames such as "libname.so", "libname.so.xx.xx", resolve to the correct
pathname of the specified library.  The use of absolute/relative paths are 
still permitted.  Current runtime linker security is maintained.  Also included
is a patch to dlfcn.3 that reflects the new behavior.

*** rtld.c.orig Fri Oct 13 19:33:32 1995
--- rtld.c      Sat Nov 11 00:23:39 1995
***************
*** 1331,1336 ****
--- 1331,1440 ----
  };
  static int dlerrno;

+ /*
+  * Populate sod struct for dlopen's call to map_obj
+  */
+ void
+ build_sod(name, sodp)
+         char            *name;
+         struct   sod    *sodp;
+ {
+         unsigned int    number;
+         unsigned int    tuplet;
+         unsigned int    strange = 0;
+         int             major, minor;
+         char            *realname, *tok, *tok1;
+
+         /* default is an absolute or relative path */
+         sodp->sod_name = (long)strdup(name);    /* strtok is destructive */
+         sodp->sod_library = 0;
+         sodp->sod_major = sodp->sod_minor = 0;
+
+         /* asking for lookup? */
+         if (strncmp((char *)sodp->sod_name, "lib", 3) == NULL) {
+
+                 /* skip over 'lib' */
+                 tok = (char *)sodp->sod_name + 3;
+
+                 /* dot guardian */
+                 if((strchr(tok, '.') != NULL) && (*(tok+strlen(tok)-1)!='.')) {
+                         tok = strtok(tok, ".");
+
+                         /* default */
+                         major = minor = -1;
+
+                         /* removed 'lib' and extensions from name */
+                         realname = tok;
+
+                         /* loop through name - parse skipping name */
+                         for(tuplet=1;((tok=strtok(NULL,"."))!=NULL);tuplet++) {
+                                 switch (tuplet) {
+                                         /* 'so' extension */
+                                         case 1:
+                                                 if(strcmp(tok, "so") != NULL)
+                                                         strange = 1;
+                                                 break;
+                                         /* major ver extension */
+                                         case 2:
+                                                 for(tok1=tok;
+                                                     (*tok1 != '\0');
+                                                     tok1++) {
+                                                         if(isdigit(*tok1))
+                                                                 number = 1;
+                                                         else {
+                                                                 number = 0;
+                                                                 break;
+                                                         }
+                                                 }
+                                                 if(number)
+                                                         major = atoi(tok);
+                                                 else {
+                                                         major = 0;
+                                                         strange = 1;
+                                                 }
+                                                 break;
+                                         /* minor ver extension */
+                                         case 3:
+                                                 for(tok1=tok;
+                                                     (*tok1 != '\0');
+                                                     tok1++) {
+                                                         if(isdigit(*tok1))
+                                                                 number = 1;
+                                                         else {
+                                                                 number = 0;
+                                                                 break;
+                                                         }
+                                                 }
+                                                 if(number)
+                                                         minor = atoi(tok);
+                                                 else {
+                                                         minor = 0;
+                                                         strange = 1;
+                                                 }
+                                                 break;
+                                         /* if we get here, it must be weird */
+                                         default:
+                                                 strange = 1;
+                                 } /* end switch */
+                         } /* end for */
+
+                         /* normal */
+                         if(!strange) {
+                                 free((char *)sodp->sod_name);
+                                 sodp->sod_name = (long)strdup(realname);
+                                 sodp->sod_library = 1;
+                                 sodp->sod_major = major;
+                                 sodp->sod_minor = minor;
+                         }
+                         /* strange */
+                         else {
+                                 free((char *)sodp->sod_name);
+                                 sodp->sod_name = (long)strdup(name);
+                         }
+                 } /* end if dots */
+         }  /* end if lookup */
+ }
+
  static void *
  __dlopen(name, mode)
        char    *name;
***************
*** 1352,1362 ****
                return NULL;
        }

!       sodp->sod_name = (long)strdup(name);
!       sodp->sod_library = 0;
!       sodp->sod_major = sodp->sod_minor = 0;

!       if ((smp = map_object(name, sodp, 0)) == NULL) {
  #ifdef DEBUG
  xprintf("%s: %s\n", name, strerror(errno));
  #endif
--- 1456,1464 ----
                return NULL;
        }

!       build_sod(name, sodp);

!       if ((smp = map_object((char *)sodp->sod_name, sodp, 0)) == NULL) {
  #ifdef DEBUG
  xprintf("%s: %s\n", name, strerror(errno));
  #endif
*** dlfcn.3.orig        Fri Oct 13 20:26:57 1995
--- dlfcn.3     Mon Nov 13 00:54:06 1995
***************
*** 41,47 ****
  .Sh SYNOPSIS
  .Fd #include <dlfcn.h>
  .Ft "void *"
! .Fn dlopen "char *path" "int mode"
  .Ft "int"
  .Fn dlclose "void *handle"
  .Ft "void *"
--- 41,47 ----
  .Sh SYNOPSIS
  .Fd #include <dlfcn.h>
  .Ft "void *"
! .Fn dlopen "char *name" "int mode"
  .Ft "int"
  .Fn dlclose "void *handle"
  .Ft "void *"
***************
*** 57,66 ****
  under program control.
  The
  .Fn dlopen
! function takes a pathname of a shared object as the first argument. The
! shared object is mapped into the address space, relocated and its external
! references are resolved in the same way as is done with the implicitly loaded
! shared libraries at program startup.
  The second argument has currently no effect, but should be set to
  .Dv DL_LAZY
  for future compatibility.
--- 57,67 ----
  under program control.
  The
  .Fn dlopen
! function takes a filename of the forms 'libname.so', 'libname.so.xx.xx' where
! xx are the major and minor revisions, or 'pathname/filename' of a shared object
! as the first argument. The shared object is mapped into the address space,
! relocated and its external references are resolved in the same way as is done
! with the implicitly loaded shared libraries at program startup.
  The second argument has currently no effect, but should be set to
  .Dv DL_LAZY
  for future compatibility.
>Audit-Trail:
>Unformatted: