Implement fdlopen(3), an rtld interface to load shared object by file

descriptor.

Requested and tested by:	des (previous version)
Reviewed by:  des, kan (previous version)
MFC after:    2 weeks
This commit is contained in:
Konstantin Belousov 2012-01-07 10:33:01 +00:00
parent 5bbe0c5357
commit 5d8aec9c02
7 changed files with 130 additions and 35 deletions

View File

@ -118,6 +118,7 @@ void *dlopen(const char *, int);
void *dlsym(void * __restrict, const char * __restrict);
#if __BSD_VISIBLE
void *fdlopen(int, int);
int dladdr(const void * __restrict, Dl_info * __restrict);
dlfunc_t dlfunc(void * __restrict, const char * __restrict);
int dlinfo(void * __restrict, int, void * __restrict);

View File

@ -95,8 +95,8 @@ MLINKS+=directory.3 closedir.3 directory.3 dirfd.3 directory.3 opendir.3 \
directory.3 fdopendir.3 \
directory.3 readdir.3 directory.3 readdir_r.3 directory.3 rewinddir.3 \
directory.3 seekdir.3 directory.3 telldir.3
MLINKS+=dlopen.3 dlclose.3 dlopen.3 dlerror.3 dlopen.3 dlfunc.3 \
dlopen.3 dlsym.3
MLINKS+=dlopen.3 fdlopen.3 dlopen.3 dlclose.3 dlopen.3 dlerror.3 \
dlopen.3 dlfunc.3 dlopen.3 dlsym.3
MLINKS+=err.3 err_set_exit.3 err.3 err_set_file.3 err.3 errc.3 err.3 errx.3 \
err.3 verr.3 err.3 verrc.3 err.3 verrx.3 err.3 vwarn.3 err.3 vwarnc.3 \
err.3 vwarnx.3 err.3 warnc.3 err.3 warn.3 err.3 warnx.3

View File

@ -382,6 +382,7 @@ FBSD_1.2 {
};
FBSD_1.3 {
fdlopen;
__FreeBSD_libc_enter_restricted_mode;
};

View File

@ -147,6 +147,15 @@ dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *),
return 0;
}
#pragma weak fdlopen
void *
fdlopen(int fd, int mode)
{
_rtld_error(sorry);
return NULL;
}
#pragma weak _rtld_atfork_pre
void
_rtld_atfork_pre(int *locks)

View File

@ -32,11 +32,12 @@
.\" @(#) dlopen.3 1.6 90/01/31 SMI
.\" $FreeBSD$
.\"
.Dd July 7, 2009
.Dd December 21, 2011
.Dt DLOPEN 3
.Os
.Sh NAME
.Nm dlopen ,
.Nm fdlopen ,
.Nm dlsym ,
.Nm dlfunc ,
.Nm dlerror ,
@ -49,6 +50,8 @@
.Ft void *
.Fn dlopen "const char *path" "int mode"
.Ft void *
.Fn fdlopen "int fd" "int mode"
.Ft void *
.Fn dlsym "void * restrict handle" "const char * restrict symbol"
.Ft dlfunc_t
.Fn dlfunc "void * restrict handle" "const char * restrict symbol"
@ -164,6 +167,36 @@ be interrogated with
.Fn dlerror .
.Pp
The
.Fn fdlopen
function is similar to
.Fn dlopen ,
but it takes the file descriptor argument
.Fa fd ,
which is used for the file operations needed to load an object
into the address space.
The file descriptor
.Fa fd
is not closed by the function regardless a result of execution,
but a duplicate of the file descriptor is.
This may be important if a
.Xr lockf 3
lock is held on the passed descriptor.
The
.Fa fd
argument -1 is interpreted as a reference to the main
executable of the process, similar to
.Va NULL
value for the
.Fa name
argument to
.Fn dlopen .
The
.Fn fdlopen
function can be used by the code that needs to perform
additional checks on the loaded objects, to prevent races with
symlinking or renames.
.Pp
The
.Fn dlsym
function
returns the address binding of the symbol described in the null-terminated
@ -354,6 +387,7 @@ option to the C language compiler.
.Sh ERRORS
The
.Fn dlopen ,
.Fn fdlopen ,
.Fn dlsym ,
and
.Fn dlfunc

View File

@ -18,6 +18,10 @@ FBSD_1.0 {
__tls_get_addr;
};
FBSD_1.3 {
fdlopen;
};
FBSDprivate_1.0 {
_rtld_thread_init;
_rtld_allocate_tls;

View File

@ -83,7 +83,7 @@ static void digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *);
static void digest_dynamic(Obj_Entry *, int);
static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
static Obj_Entry *dlcheck(void *);
static Obj_Entry *dlopen_object(const char *name, Obj_Entry *refobj,
static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj,
int lo_flags, int mode);
static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int);
static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *);
@ -103,7 +103,7 @@ static void load_filtees(Obj_Entry *, int flags, RtldLockState *);
static void unload_filtees(Obj_Entry *);
static int load_needed_objects(Obj_Entry *, int);
static int load_preload_objects(void);
static Obj_Entry *load_object(const char *, const Obj_Entry *, int);
static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int);
static void map_stacks_exec(RtldLockState *);
static Obj_Entry *obj_from_addr(const void *);
static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *);
@ -120,6 +120,7 @@ static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now,
RtldLockState *lockstate);
static int rtld_dirname(const char *, char *);
static int rtld_dirname_abs(const char *, char *);
static void *rtld_dlopen(const char *name, int fd, int mode);
static void rtld_exit(void);
static char *search_library_path(const char *, const char *);
static const void **get_program_var_addr(const char *, RtldLockState *);
@ -1544,7 +1545,7 @@ load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags)
{
for (; needed != NULL; needed = needed->next) {
needed->obj = dlopen_object(obj->strtab + needed->name, obj,
needed->obj = dlopen_object(obj->strtab + needed->name, -1, obj,
flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW : RTLD_LAZY) |
RTLD_LOCAL);
}
@ -1568,7 +1569,7 @@ process_needed(Obj_Entry *obj, Needed_Entry *needed, int flags)
Obj_Entry *obj1;
for (; needed != NULL; needed = needed->next) {
obj1 = needed->obj = load_object(obj->strtab + needed->name, obj,
obj1 = needed->obj = load_object(obj->strtab + needed->name, -1, obj,
flags & ~RTLD_LO_NOLOAD);
if (obj1 == NULL && !ld_tracing && (flags & RTLD_LO_FILTEES) == 0)
return (-1);
@ -1615,7 +1616,7 @@ load_preload_objects(void)
savech = p[len];
p[len] = '\0';
if (load_object(p, NULL, 0) == NULL)
if (load_object(p, -1, NULL, 0) == NULL)
return -1; /* XXX - cleanup */
p[len] = savech;
p += len;
@ -1625,43 +1626,68 @@ load_preload_objects(void)
return 0;
}
static const char *
printable_path(const char *path)
{
return (path == NULL ? "<unknown>" : path);
}
/*
* Load a shared object into memory, if it is not already loaded.
* Load a shared object into memory, if it is not already loaded. The
* object may be specified by name or by user-supplied file descriptor
* fd_u. In the later case, the fd_u descriptor is not closed, but its
* duplicate is.
*
* Returns a pointer to the Obj_Entry for the object. Returns NULL
* on failure.
*/
static Obj_Entry *
load_object(const char *name, const Obj_Entry *refobj, int flags)
load_object(const char *name, int fd_u, const Obj_Entry *refobj, int flags)
{
Obj_Entry *obj;
int fd = -1;
int fd;
struct stat sb;
char *path;
for (obj = obj_list->next; obj != NULL; obj = obj->next)
if (object_match_name(obj, name))
return obj;
if (name != NULL) {
for (obj = obj_list->next; obj != NULL; obj = obj->next) {
if (object_match_name(obj, name))
return (obj);
}
path = find_library(name, refobj);
if (path == NULL)
return NULL;
path = find_library(name, refobj);
if (path == NULL)
return (NULL);
} else
path = NULL;
/*
* If we didn't find a match by pathname, open the file and check
* again by device and inode. This avoids false mismatches caused
* by multiple links or ".." in pathnames.
* If we didn't find a match by pathname, or the name is not
* supplied, open the file and check again by device and inode.
* This avoids false mismatches caused by multiple links or ".."
* in pathnames.
*
* To avoid a race, we open the file and use fstat() rather than
* using stat().
*/
if ((fd = open(path, O_RDONLY)) == -1) {
_rtld_error("Cannot open \"%s\"", path);
free(path);
return NULL;
fd = -1;
if (fd_u == -1) {
if ((fd = open(path, O_RDONLY)) == -1) {
_rtld_error("Cannot open \"%s\"", path);
free(path);
return (NULL);
}
} else {
fd = dup(fd_u);
if (fd == -1) {
_rtld_error("Cannot dup fd");
free(path);
return (NULL);
}
}
if (fstat(fd, &sb) == -1) {
_rtld_error("Cannot fstat \"%s\"", path);
_rtld_error("Cannot fstat \"%s\"", printable_path(path));
close(fd);
free(path);
return NULL;
@ -1669,7 +1695,7 @@ load_object(const char *name, const Obj_Entry *refobj, int flags)
for (obj = obj_list->next; obj != NULL; obj = obj->next)
if (obj->ino == sb.st_ino && obj->dev == sb.st_dev)
break;
if (obj != NULL) {
if (obj != NULL && name != NULL) {
object_add_name(obj, name);
free(path);
close(fd);
@ -1703,20 +1729,25 @@ do_load_object(int fd, const char *name, char *path, struct stat *sbp,
*/
if (dangerous_ld_env) {
if (fstatfs(fd, &fs) != 0) {
_rtld_error("Cannot fstatfs \"%s\"", path);
return NULL;
_rtld_error("Cannot fstatfs \"%s\"", printable_path(path));
return NULL;
}
if (fs.f_flags & MNT_NOEXEC) {
_rtld_error("Cannot execute objects on %s\n", fs.f_mntonname);
return NULL;
}
}
dbg("loading \"%s\"", path);
obj = map_object(fd, path, sbp);
dbg("loading \"%s\"", printable_path(path));
obj = map_object(fd, printable_path(path), sbp);
if (obj == NULL)
return NULL;
object_add_name(obj, name);
/*
* If DT_SONAME is present in the object, digest_dynamic2 already
* added it to the object names.
*/
if (name != NULL)
object_add_name(obj, name);
obj->path = path;
digest_dynamic(obj, 0);
if (obj->z_noopen && (flags & (RTLD_LO_DLOPEN | RTLD_LO_TRACE)) ==
@ -2211,6 +2242,20 @@ dllockinit(void *context,
void *
dlopen(const char *name, int mode)
{
return (rtld_dlopen(name, -1, mode));
}
void *
fdlopen(int fd, int mode)
{
return (rtld_dlopen(NULL, fd, mode));
}
static void *
rtld_dlopen(const char *name, int fd, int mode)
{
RtldLockState lockstate;
int lo_flags;
@ -2232,7 +2277,7 @@ dlopen(const char *name, int mode)
if (ld_tracing != NULL)
lo_flags |= RTLD_LO_TRACE;
return (dlopen_object(name, obj_main, lo_flags,
return (dlopen_object(name, fd, obj_main, lo_flags,
mode & (RTLD_MODEMASK | RTLD_GLOBAL)));
}
@ -2247,7 +2292,8 @@ dlopen_cleanup(Obj_Entry *obj)
}
static Obj_Entry *
dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode)
dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags,
int mode)
{
Obj_Entry **old_obj_tail;
Obj_Entry *obj;
@ -2262,11 +2308,11 @@ dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode)
old_obj_tail = obj_tail;
obj = NULL;
if (name == NULL) {
if (name == NULL && fd == -1) {
obj = obj_main;
obj->refcount++;
} else {
obj = load_object(name, refobj, lo_flags);
obj = load_object(name, fd, refobj, lo_flags);
}
if (obj) {