diff --git a/src/dlload.c b/src/dlload.c index a4826793fb79e..cd0acdab8854c 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -190,7 +190,8 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, snprintf(relocated, PATHBUF, "%s%s", jl_options.julia_bindir, dl_path + 16); len = len - 16 + strlen(jl_options.julia_bindir); } else { - strncpy(relocated, dl_path, len); + strncpy(relocated, dl_path, PATHBUF); + relocated[PATHBUF-1] = '\0'; } for (i = 0; i < n_extensions; i++) { const char *ext = extensions[i]; diff --git a/stdlib/Libdl/test/runtests.jl b/stdlib/Libdl/test/runtests.jl index 4279e71612c43..6d2b83b28b323 100644 --- a/stdlib/Libdl/test/runtests.jl +++ b/stdlib/Libdl/test/runtests.jl @@ -222,4 +222,54 @@ let dl = C_NULL @test_skip !Libdl.dlclose(dl) # Syscall doesn't fail on Win32 end +# test DL_LOAD_PATH handling and @executable_path expansion +mktempdir() do dir + # Create a `libdcalltest` in a directory that is not on our load path + src_path = joinpath(private_libdir, "libccalltest.$(Libdl.dlext)") + dst_path = joinpath(dir, "libdcalltest.$(Libdl.dlext)") + cp(src_path, dst_path) + + # Add an absurdly long entry to the load path to verify it doesn't lead to a buffer overflow + push!(Base.DL_LOAD_PATH, joinpath(dir, join(rand('a':'z', 10000)))) + + # Add the temporary directors to load path by absolute path + push!(Base.DL_LOAD_PATH, dir) + + # Test that we can now open that file + Libdl.dlopen("libdcalltest") do dl + fptr = Libdl.dlsym(dl, :set_verbose) + @test fptr !== nothing + @test_throws ErrorException Libdl.dlsym(dl, :foo) + + fptr = Libdl.dlsym_e(dl, :set_verbose) + @test fptr != C_NULL + fptr = Libdl.dlsym_e(dl, :foo) + @test fptr == C_NULL + end + + # Skip these tests if the temporary directory is not on the same filesystem + # as the BINDIR, as in that case, a relative path will never work. + if Base.Filesystem.splitdrive(dir)[1] != Base.Filesystem.splitdrive(Sys.BINDIR)[1] + return + end + + empty!(Base.DL_LOAD_PATH) + push!(Base.DL_LOAD_PATH, joinpath(dir, join(rand('a':'z', 10000)))) + + # Add this temporary directory to our load path, now using `@executable_path` to do so. + push!(Base.DL_LOAD_PATH, joinpath("@executable_path", relpath(dir, Sys.BINDIR))) + + # Test that we can now open that file + Libdl.dlopen("libdcalltest") do dl + fptr = Libdl.dlsym(dl, :set_verbose) + @test fptr !== nothing + @test_throws ErrorException Libdl.dlsym(dl, :foo) + + fptr = Libdl.dlsym_e(dl, :set_verbose) + @test fptr != C_NULL + fptr = Libdl.dlsym_e(dl, :foo) + @test fptr == C_NULL + end +end + end