Skip to content

Commit 073666d

Browse files
authored
Make some tests not fail with uid 0 (#60033)
When running as UID 0 (system root, but also container root in various sandboxing technologies), the system ignores unix permissions. We have a number of tests that assume that a 000 mode means the file is not readable. Adjust these tests appropriately. There are more failures, but I'll look at those in a follow-up.
1 parent 946de33 commit 073666d

File tree

4 files changed

+62
-25
lines changed

4 files changed

+62
-25
lines changed

stdlib/REPL/test/replcompletions.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1249,7 +1249,10 @@ let s, c, r
12491249
@test s[r] == "tmp-execu"
12501250

12511251
c,r = test_scomplete("replcompletions-link")
1252-
@test isempty(c)
1252+
if !Sys.isunix() || Libc.getuid() != 0
1253+
# Root bypasses permissions
1254+
@test isempty(c)
1255+
end
12531256
end
12541257
finally
12551258
# If we don't fix the permissions here, our cleanup fails.

test/file.jl

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -260,13 +260,14 @@ no_error_logging(f::Function) =
260260
@test TEMP_CLEANUP_MAX[] == 3
261261
local t, f
262262
temps = String[]
263+
npending = 0
263264
# mktemp is normally cleaned up on completion
264265
mktemp(d) do path, _
265266
@test isfile(path)
266267
t = path
267268
end
268269
@test !ispath(t)
269-
@test length(TEMP_CLEANUP) == 0
270+
@test length(TEMP_CLEANUP) == npending
270271
@test TEMP_CLEANUP_MAX[] == 3
271272
# mktemp when cleanup is prevented
272273
no_error_logging() do
@@ -277,19 +278,26 @@ no_error_logging(f::Function) =
277278
t = path
278279
end
279280
end
281+
# Make deleteable again
280282
chmod(d, 0o700)
281283
close(f)
282-
@test isfile(t)
283-
@test length(TEMP_CLEANUP) == 1
284-
@test TEMP_CLEANUP_MAX[] == 3
285-
push!(temps, t)
284+
if Libc.geteuid() == 0
285+
# Root can delete anything
286+
@test !isfile(t)
287+
else
288+
npending += 1
289+
@test isfile(t)
290+
@test length(TEMP_CLEANUP) == npending
291+
@test TEMP_CLEANUP_MAX[] == 3
292+
push!(temps, t)
293+
end
286294
# mktempdir is normally cleaned up on completion
287295
mktempdir(d) do path
288296
@test isdir(path)
289297
t = path
290298
end
291299
@test !ispath(t)
292-
@test length(TEMP_CLEANUP) == 1
300+
@test length(TEMP_CLEANUP) == npending
293301
@test TEMP_CLEANUP_MAX[] == 3
294302
# mktempdir when cleanup is prevented
295303
no_error_logging() do
@@ -301,16 +309,24 @@ no_error_logging(f::Function) =
301309
t = path
302310
end
303311
end
312+
# Make deleteable again
304313
chmod(d, 0o700)
305314
close(f)
306-
@test isdir(t)
307-
@test length(TEMP_CLEANUP) == 2
308-
@test TEMP_CLEANUP_MAX[] == 3
309-
push!(temps, t)
315+
if Libc.geteuid() == 0
316+
# Root can delete anything
317+
@test !isdir(t)
318+
else
319+
@test isdir(t)
320+
npending += 1
321+
@test length(TEMP_CLEANUP) == npending
322+
@test TEMP_CLEANUP_MAX[] == 3
323+
push!(temps, t)
324+
end
310325
# make one more temp file
311326
t = mktemp()[1]
327+
npending += 1
312328
@test isfile(t)
313-
@test length(TEMP_CLEANUP) == 3
329+
@test length(TEMP_CLEANUP) == npending
314330
@test TEMP_CLEANUP_MAX[] == 3
315331
# nothing has been deleted yet
316332
for t in temps
@@ -319,8 +335,9 @@ no_error_logging(f::Function) =
319335
# another temp file triggers purge
320336
t = mktempdir()
321337
@test isdir(t)
322-
@test length(TEMP_CLEANUP) == 2
323-
@test TEMP_CLEANUP_MAX[] == 4
338+
npending = 2
339+
@test length(TEMP_CLEANUP) == npending
340+
@test TEMP_CLEANUP_MAX[] == (Libc.geteuid() == 0 ? 3 : 4)
324341
# now all the temps are gone
325342
for t in temps
326343
@test !ispath(t)
@@ -420,6 +437,9 @@ function test_stat_error(stat::Function, pth)
420437
if stat === lstat && !(pth isa AbstractString)
421438
return # no lstat for fd handles
422439
end
440+
if Libc.geteuid() == 0
441+
return # root bypasses permission checks
442+
end
423443
ex = try; stat(pth); false; catch ex; ex; end::Base.IOError
424444
@test ex.code == (pth isa AbstractString ? Base.UV_EACCES : Base.UV_EBADF)
425445
pth isa AbstractString || (pth = Base.INVALID_OS_HANDLE)
@@ -550,16 +570,23 @@ function multiple_uv_errors(pfx::AbstractString, codes::AbstractVector{<:Integer
550570
return [Base._UVError(pfx, code) for code in codes]
551571
end
552572

573+
read_linux_id_map_max(file) = parse(Int, split(strip(read(file, String)), " ", keepempty = false)[end]) % Cint
553574
if !Sys.iswindows()
554575
# chown will give an error if the user does not have permissions to change files
555576
uid = Libc.geteuid()
556577
@test stat(file).uid == uid
557578
@test uid == Libc.getuid()
579+
maxuid = maxgid = -1
580+
# Containers may have restricted uid/gid ranges
581+
if Sys.islinux() && isfile("/proc/self/uid_map")
582+
maxuid = read_linux_id_map_max("/proc/self/uid_map")
583+
maxgid = read_linux_id_map_max("/proc/self/gid_map")
584+
end
558585
if uid == 0 # root user
559-
chown(file, -2, -1) # Change the file owner to nobody
560-
@test stat(file).uid != 0
561-
chown(file, 0, -2) # Change the file group to nogroup (and owner back to root)
562-
@test stat(file).gid != 0
586+
chown(file, maxuid-1, -1) # Change the file owner to nobody
587+
@test maxuid == 1 || stat(file).uid != 0
588+
chown(file, 0, maxgid-1) # Change the file group to nogroup (and owner back to root)
589+
@test maxgid == 1 || stat(file).gid != 0
563590
@test stat(file).uid == 0
564591
@test chown(file, -1, 0) == file
565592
@test stat(file).gid == 0
@@ -1864,8 +1891,10 @@ if !Sys.iswindows()
18641891
@test !isdir(joinpath(d, "empty_outer"))
18651892

18661893
# But a non-empty directory is not
1867-
@test_throws Base.IOError rm(joinpath(d, "nonempty"); recursive=true)
1868-
chmod(joinpath(d, "nonempty"), 0o777)
1894+
if Libc.geteuid() != 0 # root can override permissions
1895+
@test_throws Base.IOError rm(joinpath(d, "nonempty"); recursive=true)
1896+
chmod(joinpath(d, "nonempty"), 0o777)
1897+
end
18691898
rm(joinpath(d, "nonempty"); recursive=true, force=true)
18701899
@test !isdir(joinpath(d, "nonempty"))
18711900
end
@@ -2032,10 +2061,10 @@ end
20322061
chmod(fpath, 0o444)
20332062
@test !Sys.isexecutable(fpath)
20342063
@test Sys.isreadable(fpath)
2035-
@test !Sys.iswritable(fpath)
2064+
@test !Sys.iswritable(fpath) skip=Libc.getuid() == 0
20362065
chmod(fpath, 0o244)
20372066
@test !Sys.isexecutable(fpath)
2038-
@test !Sys.isreadable(fpath) skip=Sys.iswindows()
2067+
@test !Sys.isreadable(fpath) skip=(Sys.iswindows() || Libc.getuid() == 0)
20392068
@test Sys.iswritable(fpath) skip=Sys.iswindows()
20402069

20412070
# Ensure that, on Windows, where inheritance is default,

test/precompile.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2249,7 +2249,7 @@ precompile_test_harness("Issue #52063") do load_path
22492249
@test e isa SystemError
22502250
@test e.prefix == "opening file or folder $(repr(fname))"
22512251
true
2252-
end
2252+
end skip = (Sys.isunix() && Libc.geteuid() == 0)
22532253
dir = mktempdir() do dir
22542254
@test include_dependency(dir) === nothing
22552255
chmod(dir, 0x000)
@@ -2259,7 +2259,7 @@ precompile_test_harness("Issue #52063") do load_path
22592259
@test e isa SystemError
22602260
@test e.prefix == "opening file or folder $(repr(dir))"
22612261
true
2262-
end
2262+
end skip = (Sys.isunix() && Libc.geteuid() == 0)
22632263
dir
22642264
end
22652265
@test try

test/sysinfo.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ if Sys.isunix()
3535
original_path = ENV["PATH"]
3636
ENV["PATH"] = string(firstdir, ":", seconddir, ":", original_path)
3737
try
38-
@test abspath(Base.Sys.which("foo")) == abspath(joinpath(seconddir, "foo"))
38+
if Libc.geteuid() == 0
39+
# Root bypasses permission checks
40+
@test abspath(Base.Sys.which("foo")) == abspath(joinpath(firstdir, "foo"))
41+
else
42+
@test abspath(Base.Sys.which("foo")) == abspath(joinpath(seconddir, "foo"))
43+
end
3944
finally
4045
# clean up
4146
chmod(firstdir, 0o777)

0 commit comments

Comments
 (0)