From b50604121db9cc6a7b1294ca040fc9a715c00359 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 18 Aug 2016 14:12:52 -0400 Subject: [PATCH] make dlopen more conservative about opening files the user didn't request (#18061) * make dlopen more conservative about opening files the user didn't request * update manual * recognize \server\foo and /foo/bar absolute paths on windows, similar to isabspath * simplify isabspath from init.c and use it in dlopen instead of duplicating --- base/libdl.jl | 6 +++++ doc/stdlib/libdl.rst | 2 ++ src/dlload.c | 60 +++++++++++++++++--------------------------- src/init.c | 10 +++----- src/julia_internal.h | 2 ++ 5 files changed, 36 insertions(+), 44 deletions(-) diff --git a/base/libdl.jl b/base/libdl.jl index bac9f95e7f353..f2edd0130f0ee 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -69,6 +69,12 @@ end Load a shared library, returning an opaque handle. +The extension given by the constant `dlext` (`.so`, `.dll`, or `.dylib`) +can be omitted from the `libfile` string, as it is automatically appended +if needed. If `libfile` is not an absolute path name, then the paths +in the array `DL_LOAD_PATH` are searched for `libfile`, followed by the +system load path. + The optional flags argument is a bitwise-or of zero or more of `RTLD_LOCAL`, `RTLD_GLOBAL`, `RTLD_LAZY`, `RTLD_NOW`, `RTLD_NODELETE`, `RTLD_NOLOAD`, `RTLD_DEEPBIND`, and `RTLD_FIRST`. These are converted to the corresponding flags of the POSIX (and/or GNU libc and/or MacOS) diff --git a/doc/stdlib/libdl.rst b/doc/stdlib/libdl.rst index 6da4dffbc60f7..2f41c87bd11ad 100644 --- a/doc/stdlib/libdl.rst +++ b/doc/stdlib/libdl.rst @@ -14,6 +14,8 @@ The names in :mod:`Base.Libdl` are not exported and need to be called e.g. as `` Load a shared library, returning an opaque handle. + The extension given by the constant ``dlext`` (``.so``\ , ``.dll``\ , or ``.dylib``\ ) can be omitted from the ``libfile`` string, as it is automatically appended if needed. If ``libfile`` is not an absolute path name, then the paths in the array ``DL_LOAD_PATH`` are searched for ``libfile``\ , followed by the system load path. + The optional flags argument is a bitwise-or of zero or more of ``RTLD_LOCAL``\ , ``RTLD_GLOBAL``\ , ``RTLD_LAZY``\ , ``RTLD_NOW``\ , ``RTLD_NODELETE``\ , ``RTLD_NOLOAD``\ , ``RTLD_DEEPBIND``\ , and ``RTLD_FIRST``\ . These are converted to the corresponding flags of the POSIX (and/or GNU libc and/or MacOS) dlopen command, if possible, or are ignored if the specified functionality is not available on the current platform. The default flags are platform specific. On MacOS the default ``dlopen`` flags are ``RTLD_LAZY|RTLD_DEEPBIND|RTLD_GLOBAL`` while on other platforms the defaults are ``RTLD_LAZY|RTLD_DEEPBIND|RTLD_LOCAL``\ . An important usage of these flags is to specify non default behavior for when the dynamic library loader binds library references to exported symbols and if the bound references are put into process local or global scope. For instance ``RTLD_LAZY|RTLD_DEEPBIND|RTLD_GLOBAL`` allows the library's symbols to be available for usage in other shared libraries, addressing situations where there are dependencies between shared libraries. .. function:: dlopen_e(libfile::AbstractString [, flags::Integer]) diff --git a/src/dlload.c b/src/dlload.c index 1966c92803f58..414e594da97fd 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -21,17 +21,15 @@ extern "C" { #endif -// The empty extension at the beginning and the end is a trick to change -// the order of the loop. #if defined(__APPLE__) -static char const *const extensions[] = { "", ".dylib", "" }; +static char const *const extensions[] = { "", ".dylib" }; #elif defined(_OS_WINDOWS_) -static char const *const extensions[] = { "", ".dll", "" }; +static char const *const extensions[] = { "", ".dll" }; extern int needsSymRefreshModuleList; #else -static char const *const extensions[] = { "", ".so", "" }; +static char const *const extensions[] = { "", ".so" }; #endif -#define N_EXTENSIONS (sizeof(extensions) / sizeof(char*) - 1) +#define N_EXTENSIONS (sizeof(extensions) / sizeof(char*)) static int endswith_extension(const char *path) { @@ -118,11 +116,10 @@ static void *jl_load_dynamic_library_(const char *modname, unsigned flags, int t int i; uv_stat_t stbuf; void *handle; - // This determines if we try the no-extension name first or last - // We want to make sure the last one we try has higher chance of being - // a real file since the error reported will otherwise be a unhelpful - // file not found error due to the extra or missing extension name. - int hasext = endswith_extension(modname); + int abspath; + // number of extensions to try — if modname already ends with the + // standard extension, then we don't try adding additional extensions + int n_extensions = endswith_extension(modname) ? 1 : N_EXTENSIONS; /* this branch returns handle of libjulia @@ -139,26 +136,15 @@ static void *jl_load_dynamic_library_(const char *modname, unsigned flags, int t #endif goto done; } - /* - this branch shortcuts absolute paths - */ -#ifdef _OS_WINDOWS_ - else if (modname[1] == ':') { -#else - else if (modname[0] == '/') { -#endif - handle = jl_dlopen(modname, flags); - if (handle) - goto done; - // bail out and show the error if file actually exists - if (jl_stat(modname, (char*)&stbuf) == 0) - goto notfound; - } + + abspath = isabspath(modname); + /* this branch permutes all base paths in DL_LOAD_PATH with all extensions - note: skip when !jl_base_module to avoid UndefVarError(:DL_LOAD_PATH) + note: skip when !jl_base_module to avoid UndefVarError(:DL_LOAD_PATH), + and also skip for absolute paths */ - else if (jl_base_module != NULL) { + if (!abspath && jl_base_module != NULL) { jl_array_t *DL_LOAD_PATH = (jl_array_t*)jl_get_global(jl_base_module, jl_symbol("DL_LOAD_PATH")); if (DL_LOAD_PATH != NULL) { size_t j; @@ -167,9 +153,8 @@ static void *jl_load_dynamic_library_(const char *modname, unsigned flags, int t size_t len = strlen(dl_path); if (len == 0) continue; - for (i=0; i < N_EXTENSIONS; i++) { - // Do the no-ext one last if hasext == 1 - const char *ext = extensions[i + hasext]; + for (i=0; i < n_extensions; i++) { + const char *ext = extensions[i]; path[0] = '\0'; if (dl_path[len-1] == PATHSEPSTRING[0]) snprintf(path, PATHBUF, "%s%s%s", dl_path, modname, ext); @@ -187,9 +172,8 @@ static void *jl_load_dynamic_library_(const char *modname, unsigned flags, int t } // now fall back and look in default library paths, for all extensions - for(i=0; i < N_EXTENSIONS; i++) { - // Do the no-ext one last if hasext == 1 - const char *ext = extensions[i + hasext]; + for(i=0; i < n_extensions; i++) { + const char *ext = extensions[i]; path[0] = '\0'; snprintf(path, PATHBUF, "%s%s", modname, ext); handle = jl_dlopen(path, flags); @@ -199,9 +183,11 @@ static void *jl_load_dynamic_library_(const char *modname, unsigned flags, int t #if defined(__linux__) || defined(__FreeBSD__) // check map of versioned libs from "libX" to full soname "libX.so.ver" - handle = jl_dlopen_soname(modname, strlen(modname), flags); - if (handle) - goto done; + if (!abspath && n_extensions > 1) { // soname map only works for libX + handle = jl_dlopen_soname(modname, strlen(modname), flags); + if (handle) + goto done; + } #endif notfound: diff --git a/src/init.c b/src/init.c index d4bf640a181fe..4538e99b2b47f 100644 --- a/src/init.c +++ b/src/init.c @@ -438,13 +438,9 @@ int isabspath(const char *in) if (c0 == '/' || c0 == '\\') { return 1; // absolute path relative to %CD% (current drive), or UNC } - else { - int s = strlen(in); - if (s > 2) { - char c1 = in[1]; - char c2 = in[2]; - if (c1 == ':' && (c2 == '/' || c2 == '\\')) return 1; // absolute path - } + else if (c0 && in[1] == ':') { + char c2 = in[2]; + return c2 == '/' || c2 == '\\'; // absolute path with drive name } #else if (in[0] == '/') return 1; // absolute path diff --git a/src/julia_internal.h b/src/julia_internal.h index fc9514260c02f..39234ee65e79b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -763,6 +763,8 @@ STATIC_INLINE void *jl_get_frame_addr(void) JL_DLLEXPORT jl_array_t *jl_array_cconvert_cstring(jl_array_t *a); +int isabspath(const char *in); + #ifdef __cplusplus } #endif