Skip to content

Commit 670d6ab

Browse files
martinholtersstaticfloat
authored andcommitted
Avoid potential buffer overflow/missing zero termination of string (JuliaLang#36408)
Co-authored-by: Elliot Saba <staticfloat@gmail.com>
1 parent 5dd551f commit 670d6ab

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

src/dlload.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags,
190190
snprintf(relocated, PATHBUF, "%s%s", jl_options.julia_bindir, dl_path + 16);
191191
len = len - 16 + strlen(jl_options.julia_bindir);
192192
} else {
193-
strncpy(relocated, dl_path, len);
193+
strncpy(relocated, dl_path, PATHBUF);
194+
relocated[PATHBUF-1] = '\0';
194195
}
195196
for (i = 0; i < n_extensions; i++) {
196197
const char *ext = extensions[i];

stdlib/Libdl/test/runtests.jl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,54 @@ let dl = C_NULL
222222
@test_skip !Libdl.dlclose(dl) # Syscall doesn't fail on Win32
223223
end
224224

225+
# test DL_LOAD_PATH handling and @executable_path expansion
226+
mktempdir() do dir
227+
# Create a `libdcalltest` in a directory that is not on our load path
228+
src_path = joinpath(private_libdir, "libccalltest.$(Libdl.dlext)")
229+
dst_path = joinpath(dir, "libdcalltest.$(Libdl.dlext)")
230+
cp(src_path, dst_path)
231+
232+
# Add an absurdly long entry to the load path to verify it doesn't lead to a buffer overflow
233+
push!(Base.DL_LOAD_PATH, joinpath(dir, join(rand('a':'z', 10000))))
234+
235+
# Add the temporary directors to load path by absolute path
236+
push!(Base.DL_LOAD_PATH, dir)
237+
238+
# Test that we can now open that file
239+
Libdl.dlopen("libdcalltest") do dl
240+
fptr = Libdl.dlsym(dl, :set_verbose)
241+
@test fptr !== nothing
242+
@test_throws ErrorException Libdl.dlsym(dl, :foo)
243+
244+
fptr = Libdl.dlsym_e(dl, :set_verbose)
245+
@test fptr != C_NULL
246+
fptr = Libdl.dlsym_e(dl, :foo)
247+
@test fptr == C_NULL
248+
end
249+
250+
# Skip these tests if the temporary directory is not on the same filesystem
251+
# as the BINDIR, as in that case, a relative path will never work.
252+
if Base.Filesystem.splitdrive(dir)[1] != Base.Filesystem.splitdrive(Sys.BINDIR)[1]
253+
return
254+
end
255+
256+
empty!(Base.DL_LOAD_PATH)
257+
push!(Base.DL_LOAD_PATH, joinpath(dir, join(rand('a':'z', 10000))))
258+
259+
# Add this temporary directory to our load path, now using `@executable_path` to do so.
260+
push!(Base.DL_LOAD_PATH, joinpath("@executable_path", relpath(dir, Sys.BINDIR)))
261+
262+
# Test that we can now open that file
263+
Libdl.dlopen("libdcalltest") do dl
264+
fptr = Libdl.dlsym(dl, :set_verbose)
265+
@test fptr !== nothing
266+
@test_throws ErrorException Libdl.dlsym(dl, :foo)
267+
268+
fptr = Libdl.dlsym_e(dl, :set_verbose)
269+
@test fptr != C_NULL
270+
fptr = Libdl.dlsym_e(dl, :foo)
271+
@test fptr == C_NULL
272+
end
273+
end
274+
225275
end

0 commit comments

Comments
 (0)