Skip to content

Commit c3c51ba

Browse files
authored
Merge pull request #1755 from giuseppe/add-more-tests
add new tests
2 parents 2924a20 + fbd8ea8 commit c3c51ba

File tree

4 files changed

+227
-4
lines changed

4 files changed

+227
-4
lines changed

tests/init.c

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,9 @@ cat (char *file)
251251
}
252252

253253
static int
254-
open_only (char *file)
254+
open_only (char *file, int flags)
255255
{
256-
int fd = open (file, O_RDONLY);
256+
int fd = open (file, flags);
257257
if (fd >= 0)
258258
{
259259
close (fd);
@@ -541,7 +541,14 @@ main (int argc, char **argv)
541541
{
542542
if (argc < 3)
543543
error (EXIT_FAILURE, 0, "'open' requires an argument");
544-
return open_only (argv[2]);
544+
return open_only (argv[2], O_RDONLY);
545+
}
546+
547+
if (strcmp (argv[1], "openwronly") == 0)
548+
{
549+
if (argc < 3)
550+
error (EXIT_FAILURE, 0, "'openwronly' requires an argument");
551+
return open_only (argv[2], O_WRONLY);
545552
}
546553

547554
if (strcmp (argv[1], "access") == 0)
@@ -599,6 +606,45 @@ main (int argc, char **argv)
599606
return 0;
600607
}
601608

609+
if (strcmp (argv[1], "isfifo") == 0)
610+
{
611+
struct stat st;
612+
if (argc < 3)
613+
error (EXIT_FAILURE, 0, "'isfifo' requires a path argument");
614+
if (stat (argv[2], &st) < 0)
615+
error (EXIT_FAILURE, errno, "stat %s", argv[2]);
616+
if (S_ISFIFO (st.st_mode))
617+
exit (0);
618+
else
619+
exit (1);
620+
}
621+
622+
if (strcmp (argv[1], "ischar") == 0)
623+
{
624+
struct stat st;
625+
if (argc < 3)
626+
error (EXIT_FAILURE, 0, "'ischar' requires a path argument");
627+
if (stat (argv[2], &st) < 0)
628+
error (EXIT_FAILURE, errno, "stat %s", argv[2]);
629+
if (S_ISCHR (st.st_mode))
630+
exit (0);
631+
else
632+
exit (1);
633+
}
634+
635+
if (strcmp (argv[1], "isblock") == 0)
636+
{
637+
struct stat st;
638+
if (argc < 3)
639+
error (EXIT_FAILURE, 0, "'isblock' requires a path argument");
640+
if (stat (argv[2], &st) < 0)
641+
error (EXIT_FAILURE, errno, "stat %s", argv[2]);
642+
if (S_ISBLK (st.st_mode))
643+
exit (0);
644+
else
645+
exit (1);
646+
}
647+
602648
if (strcmp (argv[1], "owner") == 0)
603649
{
604650
struct stat st;
@@ -789,7 +835,11 @@ main (int argc, char **argv)
789835
while (ret < 0 && errno == EINTR);
790836
if (ret < 0)
791837
return ret;
792-
return status;
838+
if (WIFEXITED (status))
839+
return WEXITSTATUS (status);
840+
if (WIFSIGNALED (status))
841+
return 128 + WTERMSIG (status);
842+
return EXIT_FAILURE;
793843
}
794844
return ls (argv[2]);
795845
}

tests/test_devices.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,98 @@ def test_net_devices():
288288

289289
return 0
290290

291+
def test_mknod_fifo_device():
292+
if is_rootless():
293+
return 77
294+
295+
conf = base_config()
296+
add_all_namespaces(conf)
297+
conf['process']['args'] = ['/init', 'isfifo', '/dev/testfifo']
298+
conf['linux']['devices'] = [
299+
{"path": "/dev/testfifo", "type": "p", "fileMode": 0o0660, "uid": 1, "gid": 2}
300+
]
301+
try:
302+
run_and_get_output(conf)
303+
except Exception as e:
304+
sys.stderr.write(f"test_mknod_fifo_device failed: %s\n" % e)
305+
return -1
306+
return 0
307+
308+
def test_mknod_char_device():
309+
if is_rootless():
310+
return 77
311+
312+
conf = base_config()
313+
add_all_namespaces(conf)
314+
conf['process']['args'] = ['/init', 'ischar', '/dev/testchar']
315+
conf['linux']['devices'] = [
316+
{"path": "/dev/testchar", "type": "c", "major": 251, "minor": 1, "fileMode": 0o0640, "uid": 3, "gid": 4}
317+
]
318+
try:
319+
run_and_get_output(conf)
320+
except Exception as e:
321+
sys.stderr.write(f"test_mknod_char_device failed: {e}\n")
322+
return -1
323+
return 0
324+
325+
def test_allow_device_read_only():
326+
if is_rootless():
327+
return 77
328+
329+
try:
330+
# Best effort load
331+
subprocess.run(["modprobe", "null_blk", "nr_devices=1"])
332+
except:
333+
pass
334+
try:
335+
st = os.stat("/dev/nullb0")
336+
major, minor = os.major(st.st_rdev), os.minor(st.st_rdev)
337+
except:
338+
return 77
339+
340+
conf = base_config()
341+
add_all_namespaces(conf)
342+
343+
conf['linux']['devices'] = [{
344+
"path": "/dev/controlledchar",
345+
"type": "b",
346+
"major": major,
347+
"minor": minor,
348+
"fileMode": 0o0666
349+
}]
350+
conf['linux']['resources'] = {
351+
"devices": [
352+
{"allow": False, "access": "rwm"},
353+
{"allow": True, "type": "b", "major": major, "minor": minor, "access": "r"},
354+
]
355+
}
356+
357+
conf['process']['args'] = ['/init', 'open', '/dev/controlledchar']
358+
try:
359+
run_and_get_output(conf)
360+
except Exception as e:
361+
sys.stderr.write(f"test_allow_device_read_only failed: %s\n" % e)
362+
return -1
363+
364+
conf['process']['args'] = ['/init', 'openwronly', '/dev/controlledchar']
365+
try:
366+
run_and_get_output(conf)
367+
sys.stderr.write("test_allow_device_read_only: write access was unexpectedly allowed.\n")
368+
return 1
369+
except Exception as e:
370+
output_str = getattr(e, 'output', b'').decode(errors='ignore')
371+
if "Operation not permitted" in output_str or "Permission denied" in output_str:
372+
return 0
373+
else:
374+
sys.stderr.write(f"test_allow_device_read_only (write attempt) failed with: %s, output: %s\n" % (e, output_str))
375+
return 1
376+
377+
return 1
378+
291379
all_tests = {
380+
"mknod-fifo-device": test_mknod_fifo_device,
381+
"mknod-char-device": test_mknod_char_device,
382+
"allow-device-read-only": test_allow_device_read_only,
292383
"owner-device" : test_owner_device,
293384
"deny-devices" : test_deny_devices,
294385
"allow-device" : test_allow_device,

tests/test_pid.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,35 @@ def test_pid_user():
3939
return 0
4040
return -1
4141

42+
def test_pid_host_namespace():
43+
if is_rootless():
44+
return 77
45+
conf = base_config()
46+
conf['process']['args'] = ['/init', 'cat', '/proc/self/status']
47+
# No PID namespace is added. Expect PID to not be 1.
48+
out, _ = run_and_get_output(conf)
49+
pid = parse_proc_status(out)['Pid']
50+
if pid != "1":
51+
return 0
52+
return -1
53+
54+
def test_pid_ppid_is_zero():
55+
conf = base_config()
56+
conf['process']['args'] = ['/init', 'cat', '/proc/self/status']
57+
add_all_namespaces(conf)
58+
out, _ = run_and_get_output(conf)
59+
status = parse_proc_status(out)
60+
pid = status.get('Pid')
61+
ppid = status.get('PPid')
62+
if pid == "1" and ppid == "0":
63+
return 0
64+
return -1
65+
4266
all_tests = {
4367
"pid" : test_pid,
4468
"pid-user" : test_pid_user,
69+
"pid-host-namespace" : test_pid_host_namespace,
70+
"pid-ppid-is-zero" : test_pid_ppid_is_zero,
4571
}
4672

4773
if __name__ == "__main__":

tests/test_uid_gid.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,68 @@ def test_keep_groups():
114114
return -1
115115
return 0
116116

117+
def test_additional_gids():
118+
if is_rootless():
119+
return 77
120+
conf = base_config()
121+
conf['process']['args'] = ['/init', 'cat', '/proc/self/status']
122+
add_all_namespaces(conf)
123+
conf['process']['user']['uid'] = 1000
124+
conf['process']['user']['gid'] = 1000
125+
conf['process']['user']['additionalGids'] = [2000, 3000]
126+
out, _ = run_and_get_output(conf)
127+
proc_status = parse_proc_status(out)
128+
129+
gids_status = proc_status['Gid'].split()
130+
for g_id in gids_status:
131+
if g_id != "1000":
132+
return -1
133+
134+
groups_str = proc_status.get('Groups', "")
135+
actual_supplementary_groups = set()
136+
if groups_str:
137+
actual_supplementary_groups = set(groups_str.split())
138+
139+
expected_supplementary_groups = {"2000", "3000"}
140+
141+
if actual_supplementary_groups != expected_supplementary_groups:
142+
return -2
143+
return 0
144+
145+
def test_umask():
146+
if is_rootless():
147+
pass
148+
149+
conf = base_config()
150+
add_all_namespaces(conf)
151+
152+
test_umask_octal_str = "0027"
153+
test_umask_int = 0o027
154+
155+
conf['process']['user']['umask'] = test_umask_int
156+
conf['process']['args'] = ['/init', 'cat', '/proc/self/status']
157+
158+
out, _ = run_and_get_output(conf)
159+
proc_status = parse_proc_status(out)
160+
161+
if 'Umask' not in proc_status:
162+
return -1
163+
164+
umask_from_status = proc_status['Umask']
165+
166+
if umask_from_status != test_umask_octal_str:
167+
return -2
168+
169+
return 0
170+
117171
all_tests = {
118172
"uid" : test_uid,
119173
"gid" : test_gid,
120174
"userns-full-mapping" : test_userns_full_mapping,
121175
"no-groups" : test_no_groups,
122176
"keep-groups" : test_keep_groups,
177+
"additional-gids": test_additional_gids,
178+
"umask": test_umask,
123179
}
124180

125181
if __name__ == "__main__":

0 commit comments

Comments
 (0)