diff --git a/capsule/capsule-dlmopen.c b/capsule/capsule-dlmopen.c index b3d8cb49cfbdc6098d876236e471692a7eb83a00..e8508c291608ce91bbccd0e9882e30ab2ce3122d 100644 --- a/capsule/capsule-dlmopen.c +++ b/capsule/capsule-dlmopen.c @@ -913,8 +913,112 @@ cleanup_ldlibs (ldlibs_t *ldlibs) ldlibs->error = NULL; } +static const char * +_rtldstr(int flag) +{ + char flags[160] = { 0 }; + char *f = &flags[0]; + + if( !flag) + return "LOCAL"; + +#define RTLDFLAGSTR(x) \ + if( x & flag ) f += snprintf(f, &flags[80] - f, " %s", & #x [5]) + + RTLDFLAGSTR(RTLD_LAZY); + RTLDFLAGSTR(RTLD_NOW); + RTLDFLAGSTR(RTLD_NOLOAD); + RTLDFLAGSTR(RTLD_DEEPBIND); + RTLDFLAGSTR(RTLD_GLOBAL); + RTLDFLAGSTR(RTLD_NODELETE); + + return ( flags[0] == ' ' ) ? &flags[1] : &flags[0]; +} + +// And now we actually open everything we have found, in reverse +// dependency order (which prevents dlmopen from going and finding +// DT_NEEDED values from outside the capsule), which it will do +// if we don't work backwards: +static void * +load_ldlibs (ldlibs_t *ldlibs, Lmid_t *namespace, int flag, int *errcode, char **error) +{ + int go; + Lmid_t lm = (*namespace > 0) ? *namespace : LM_ID_NEWLM; + void *ret = NULL; + + if( !flag ) + flag = RTLD_LAZY; + + do + { + go = 0; + + for( int j = 0; j < DSO_LIMIT; j++ ) + { + // reached the end of the list + if( !ldlibs->needed[j].name ) + continue; + + // library has no further dependencies which have not already + // been satisfied (except for the libc and linker DSOs), + // this means we can safely open it without dlmopen accidentally + // pulling in DSOs from outside the encapsulated tree: + if( ldlibs->needed[j].depcount == 0 ) + { + const char *path = ldlibs->needed[j].path; + go++; + + ldlib_debug( ldlibs, "DLMOPEN %p %s %s", (void *)lm, _rtldstr(flag), path ); + + // The actual dlmopen. If this was the first one, it may + // have created a new link map id, wich we record later on: + + // note that since we do the opens in reverse dependency order, + // the _last_ one we open will be the DSO we actually asked for + // so if we succeed, ret has to contain the right handle. + ret = dlmopen( lm, path, flag ); + + if( !ret ) + { + if( error ) + *error = dlerror(); + + if( errcode ) + *errcode = EINVAL; + + return NULL; + } + + // If this was the first dlmopen, record the new LM Id + // for return to our caller: + if( lm == LM_ID_NEWLM ) + { + dlinfo( ret, RTLD_DI_LMID, namespace ); + lm = *namespace; + ldlib_debug( ldlibs, "new Lmid_t handle %p\n", (void *)lm ); + } + + // go through the map of DSOs and reduce the dependency + // count for any DSOs which had the current DSO as a dep: + for( int k = 0; k < DSO_LIMIT; k++ ) + if( ldlibs->needed[j].requestors[k] ) + ldlibs->needed[k].depcount--; + + clear_needed( &ldlibs->needed[j] ); + } + } + } while (go); + + return ret; +} + static void -init_ldlibs (ldlibs_t *ldlibs, const char **exclude, int dbg) +init_ldlibs (ldlibs_t *ldlibs, + const char **exclude, + const char *prefix, + int dbg, + int *errcode, + char **error) { memset( ldlibs, 0, sizeof(ldlibs_t) ); ldlibs->cache_fd = -1; @@ -924,8 +1028,41 @@ init_ldlibs (ldlibs_t *ldlibs, const char **exclude, int dbg) ldlibs->exclude = exclude; ldlibs->debug = dbg; + if( errcode ) + *errcode = 0; + for( int x = 0; x < DSO_LIMIT; x++ ) ldlibs->needed[x].fd = -1; + + // ================================================================== + // set up the path prefix at which we expect to find the encapsulated + // library and its ld.so.cache and dependencies and so forth: + if( prefix ) + { + size_t prefix_len = strlen( prefix ); + ssize_t space = PATH_MAX - prefix_len; + + // if we don't have at least this much space it's not + // going to work out: + if( (space - strlen( "/usr/lib/libx.so.x" )) <= 0 ) + { + if( error ) + *error = strdup( "capsule_dlmopen: prefix is too large" ); + + if( errcode ) + *errcode = ENAMETOOLONG; + + return; + } + + safe_strncpy( ldlibs->prefix.path, prefix, PATH_MAX ); + ldlibs->prefix.len = prefix_len; + } + else + { + ldlibs->prefix.path[0] = '\0'; + ldlibs->prefix.len = 0; + } } // ========================================================================== @@ -1153,10 +1290,7 @@ capsule_dlmopen (const char *dso, char **error) { void *ret = NULL; - Lmid_t lm = (*namespace > 0) ? *namespace : LM_ID_NEWLM; - ldlibs_t ldlibs = {}; - - init_ldlibs( &ldlibs, exclude, dbg ); + ldlibs_t ldlibs = { 0 }; if( elf_version(EV_CURRENT) == EV_NONE ) { @@ -1169,35 +1303,10 @@ capsule_dlmopen (const char *dso, return NULL; } - // ================================================================== - // set up the path prefix at which we expect to find the encapsulated - // library and its ld.so.cache and dependencies and so forth: - if( prefix ) - { - size_t prefix_len = strlen( prefix ); - ssize_t space = PATH_MAX - prefix_len; + init_ldlibs( &ldlibs, exclude, prefix, dbg, errcode, error ); - // if we don't have at least this much space it's not - // going to work out: - if( (space - strlen( "/usr/lib/libx.so.x" )) <= 0 ) - { - if( error ) - *error = strdup( "capsule_dlmopen: prefix is too large" ); - - if( errcode ) - *errcode = ENAMETOOLONG; - - return NULL; - } - - safe_strncpy( ldlibs.prefix.path, prefix, PATH_MAX ); - ldlibs.prefix.len = prefix_len; - } - else - { - ldlibs.prefix.path[0] = '\0'; - ldlibs.prefix.len = 0; - } + if( errcode && *errcode ) + return NULL; // ================================================================== // read in the ldo.so.cache - this will contain all architectures @@ -1275,68 +1384,11 @@ capsule_dlmopen (const char *dso, } // ================================================================== - // And now we actually open everything we have found, in reverse - // dependency order (which prevents dlmopen from going and finding - // DT_NEEDED values from outside the capsule), which it will do - // if we don't work backwards. - int go; - - do - { - go = 0; + // load the stack of DSOs we need: + ret = load_ldlibs( &ldlibs, namespace, 0, errcode, error ); - for( int j = 0; j < DSO_LIMIT; j++ ) - { - // reached the end of the list - if( !ldlibs.needed[j].name ) - continue; - - // library has no further dependencies which have not already - // been satisfied (except for the libc and linker DSOs), - // this means we can safely open it without dlmopen accidentally - // pulling in DSOs from outside the encapsulated tree: - if( ldlibs.needed[j].depcount == 0 ) - { - const char *path = ldlibs.needed[j].path; - go++; - - ldlib_debug( &ldlibs, "DLMOPEN %s %p", path, (void *)lm ); - - // The actual dlmopen. If this was the first one, it may - // have created a new link map id, wich we record later on: - ret = dlmopen( lm, path, RTLD_LAZY ); - - if( !ret ) - { - if( error ) - *error = dlerror(); - - if( errcode ) - *errcode = EINVAL; - - goto cleanup; - } - - // If this was the first dlmopen, record the new LM Id - // for return to our caller: - if( lm == LM_ID_NEWLM ) - { - dlinfo( ret, RTLD_DI_LMID, namespace ); - lm = *namespace; - - if( dbg ) - debug( "new Lmid_t handle %p\n", (void *)lm ); - } - - // go through the map of DSOs and reduce the dependency - // count for any DSOs which had the current DSO as a dep: - for( int k = 0; k < DSO_LIMIT; k++ ) - if( ldlibs.needed[j].requestors[k] ) - ldlibs.needed[k].depcount--; - clear_needed( &ldlibs.needed[j] ); - } - } - } while (go); + if( !ret ) + goto cleanup; // TODO: failure in the dlopen fixup phase should probably be fatal: if( ret != NULL && // no errors so far