Skip to content

Note why we don't call free after strdup, tweak runguard test script #2896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions judge/runguard.cc
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,8 @@ void setrestrictions()

/* Set additional environment variables. */
for (const auto &tokens : environment_variables) {
// Note that we explicitly *do not* free the string created by
// strdup as putenv does not copy that string, but uses it as is.
char *token = strtok(strdup(tokens.c_str()), ";");
while (token != nullptr) {
verbose("setting environment variable: %s", token);
Expand Down
71 changes: 36 additions & 35 deletions judge/runguard_test/runguard_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1

RUNGUARD=../runguard
RUNGUARD_OPTIONS='-u domjudge-run-0'
LOG1="$(mktemp)"
LOG2="$(mktemp)"
# shellcheck disable=SC2154
Expand Down Expand Up @@ -66,47 +67,47 @@ test_no_sudo() {
}

test_ls() {
exec_check_success sudo $RUNGUARD -u domjudge-run-0 ls
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS ls
expect_stdout "runguard_test.sh"
}

test_walltime_limit() {
exec_check_success sudo $RUNGUARD -u domjudge-run-0 -t 2 sleep 1
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -t 2 sleep 1

exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -t 2 sleep 3
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -t 2 sleep 3
expect_stderr "timelimit exceeded"
expect_stderr "hard wall time"
}

test_cputime_limit() {
# 2 threads, ~3s of CPU time, gives ~1.5s of wall time.
exec_check_success sudo $RUNGUARD -u domjudge-run-0 -C 3.1 ./threads 2 3
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -C 3.1 ./threads 2 3

# Now also limiting wall time to 2s.
exec_check_success sudo $RUNGUARD -u domjudge-run-0 -C 3.1 -t 2 ./threads 2 3
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -C 3.1 -t 2 ./threads 2 3

# Some failing cases.
exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -C 2.9 ./threads 2 3
exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -C 3.1 -t 1.4 ./threads 2 3
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -C 2.9 ./threads 2 3
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -C 3.1 -t 1.4 ./threads 2 3
}

test_cputime_pinning() {
# 2 threads, ~3s of CPU time, with one core we are out of luck...
exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -C 3.1 -t 2 -P 1 ./threads 2 3
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -C 3.1 -t 2 -P 1 ./threads 2 3
# ...but with two cores it works.
exec_check_success sudo $RUNGUARD -u domjudge-run-0 -C 3.1 -t 2 -P 0-1 ./threads 2 3
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -C 3.1 -t 2 -P 0-1 ./threads 2 3
}

test_streamsize() {
exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -t 1 -s 123 yes DOMjudge
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -t 1 -s 123 yes DOMjudge
expect_stdout "DOMjudge"
limit=$((123*1024))
actual=$(wc -c < "$LOG1")
[ $limit -eq $actual ] || fail "stdout not limited to ${limit}B, but wrote ${actual}B"
}

test_streamsize_stderr() {
exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -t 1 -s 42 ./fill-stderr.sh
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -t 1 -s 42 ./fill-stderr.sh
expect_stderr "DOMjudge"
# Allow 100 bytes extra, for the runguard time limit message.
limit=$((42*1024 + 100))
Expand All @@ -119,15 +120,15 @@ test_redir_stdout() {
chmod go+rwx "$stdout"

# Basic test.
exec_check_success sudo $RUNGUARD -u domjudge-run-0 -o "$stdout" echo 'foobar'
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -o "$stdout" echo 'foobar'
grep -q "foobar" "$stdout" || fail "did not find expected 'foobar' in redirect stdout"

# Verify that stdout is empty.
actual=$(wc -c < "$LOG1")
[ $actual -eq 0 ] || fail "stdout should be empty, but contains ${actual}B"

# This will fail because of the timeout.
exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -t 1 -s 23 -o "$stdout" yes DOMjudge
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -t 1 -s 23 -o "$stdout" yes DOMjudge
expect_stderr "timelimit exceeded"
expect_stderr "hard wall time"

Expand All @@ -149,7 +150,7 @@ test_redir_stderr() {
chmod go+rwx "$stderr"

# This will fail because of the timeout.
exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -t 1 -s 11 -e "$stderr" ./fill-stderr.sh
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -t 1 -s 11 -e "$stderr" ./fill-stderr.sh
expect_stderr "timelimit exceeded"
expect_stderr "hard wall time"

Expand All @@ -173,47 +174,47 @@ test_rootdir_changedir() {
cp hello "$almost_empty_dir"/
ln -sf /hello "$almost_empty_dir"/exists/foo

exec_check_success sudo $RUNGUARD -u domjudge-run-0 -r "$almost_empty_dir" ./hello
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -r "$almost_empty_dir" ./hello
expect_stdout "Hello DOMjudge"

exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -r "$almost_empty_dir" -d doesnotexist /hello
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -r "$almost_empty_dir" -d doesnotexist /hello
expect_stderr "cannot chdir to \`doesnotexist' in chroot"

exec_check_success sudo $RUNGUARD -u domjudge-run-0 -r "$almost_empty_dir" -d exists /hello
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -r "$almost_empty_dir" -d exists /hello
expect_stdout "Hello DOMjudge"

exec_check_success sudo $RUNGUARD -u domjudge-run-0 -r "$almost_empty_dir" -d exists ./foo
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -r "$almost_empty_dir" -d exists ./foo
expect_stdout "Hello DOMjudge"

exec_check_success sudo $RUNGUARD -u domjudge-run-0 -r "$almost_empty_dir" -d exists /exists/foo
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -r "$almost_empty_dir" -d exists /exists/foo
expect_stdout "Hello DOMjudge"
}

test_memsize() {
# This is slightly over the limit as there is other stuff to be allocated as well.
exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -m 1024 ./mem $((1024*1024))
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -m 1024 ./mem $((1024*1024))

exec_check_success sudo $RUNGUARD -u domjudge-run-0 -m 1500 ./mem $((1024*1024))
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -m 1500 ./mem $((1024*1024))
expect_stdout "mem = 1048576"

exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -m $((1024*1024)) ./mem $((1024*1024*1024))
exec_check_success sudo $RUNGUARD -u domjudge-run-0 -m $((1024*1024 + 10000)) ./mem $((1024*1024*1024))
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -m $((1024*1024)) ./mem $((1024*1024*1024))
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -m $((1024*1024 + 10000)) ./mem $((1024*1024*1024))
expect_stdout "mem = 1073741824"
}

test_envvars() {
exec_check_success sudo $RUNGUARD -u domjudge-run-0 ./print_envvars.py
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS ./print_envvars.py
expect_stdout "COUNT: 2."
expect_stdout "PATH="
expect_stdout "LC_CTYPE="
not_expect_stdout "DOMjudge"

exec_check_success sudo $RUNGUARD -u domjudge-run-0 -E ./print_envvars.py
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -E ./print_envvars.py
expect_stdout "HOME="
expect_stdout "USER="
expect_stdout "SHELL="

exec_check_success sudo $RUNGUARD -u domjudge-run-0 -V"DOMjudgeA=A;DOMjudgeB=BB" -V"DOMjudgeC=CCC" ./print_envvars.py
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -V"DOMjudgeA=A;DOMjudgeB=BB" -V"DOMjudgeC=CCC" ./print_envvars.py
expect_stdout "COUNT: 5."
expect_stdout "DOMjudgeA=A"
expect_stdout "DOMjudgeB=BB"
Expand All @@ -224,18 +225,18 @@ test_envvars() {
}

test_nprocs() {
exec_check_success sudo $RUNGUARD -u domjudge-run-0 ./forky.sh
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS ./forky.sh
expect_stdout 31

exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -p 16 ./forky.sh
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -p 16 ./forky.sh
expect_stdout 15
not_expect_stdout 16
not_expect_stdout 31
expect_stderr "fork: retry: Resource temporarily unavailable"
}

test_meta() {
exec_check_success sudo $RUNGUARD -u domjudge-run-0 -t 2 -M "$META" sleep 1
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -t 2 -M "$META" sleep 1
expect_meta 'wall-time: 1.0'
expect_meta 'cpu-time: 0.0'
expect_meta 'sys-time: 0.0'
Expand All @@ -245,27 +246,27 @@ test_meta() {
expect_meta 'stdout-bytes: 0'
expect_meta 'stderr-bytes: 0'

exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -M "$META" false
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -M "$META" false
expect_meta 'exitcode: 1'

# shellcheck disable=SC2024
echo "DOMjudge" | sudo $RUNGUARD -u domjudge-run-0 -t 2 -M "$META" rev > "$LOG1" 2> "$LOG2"
echo "DOMjudge" | sudo $RUNGUARD $RUNGUARD_OPTIONS -t 2 -M "$META" rev > "$LOG1" 2> "$LOG2"
expect_meta 'wall-time: 0.0'
expect_meta 'stdout-bytes: 9'
expect_stdout "egdujMOD"

exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -C 3.1 -t 1.4 -M "$META" ./threads 2 3
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -C 3.1 -t 1.4 -M "$META" ./threads 2 3
expect_meta 'exitcode: 143'
expect_meta 'signal: 14'
expect_meta 'wall-time: 1.5'
expect_meta 'time-result: hard-timelimit'

exec_check_success sudo $RUNGUARD -u domjudge-run-0 -C 1:5 -M "$META" ./threads 2 3
exec_check_success sudo $RUNGUARD $RUNGUARD_OPTIONS -C 1:5 -M "$META" ./threads 2 3
expect_meta 'time-used: cpu-time'
expect_meta 'time-result: soft-timelimit'
expect_meta 'exitcode: 0'

exec_check_fail sudo $RUNGUARD -u domjudge-run-0 -t 1 -s 3 -M "$META" ./fill-stderr.sh
exec_check_fail sudo $RUNGUARD $RUNGUARD_OPTIONS -t 1 -s 3 -M "$META" ./fill-stderr.sh
# We expect stderr-bytes to have a non-zero value.
expect_meta 'stderr-bytes: '
grep -q 'stderr-bytes: 0' "$META" && fail ""
Expand Down
Loading