From d380de39f039cf23a992b08c506cc200a7cf6292 Mon Sep 17 00:00:00 2001 From: Austin Morton Date: Mon, 20 May 2019 20:50:48 -0400 Subject: Add deterministic-exit-code option to ensure firejail exits with the first childs exit code regardless of the termination ordering of orphaned children --- src/firejail/firejail.h | 1 + src/firejail/main.c | 4 ++ src/firejail/profile.c | 5 +++ src/firejail/sandbox.c | 7 +++- src/firejail/usage.c | 1 + src/man/firejail-profile.txt | 4 ++ src/man/firejail.txt | 4 ++ test/environment/deterministic-exit-code.exp | 55 ++++++++++++++++++++++++++++ test/environment/environment.sh | 3 ++ 9 files changed, 82 insertions(+), 2 deletions(-) create mode 100755 test/environment/deterministic-exit-code.exp diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index e0f3a6a16..b2083d6ad 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -315,6 +315,7 @@ extern int arg_notv; // --notv extern int arg_nodvd; // --nodvd extern int arg_nou2f; // --nou2f extern int arg_nodbus; // -nodbus +extern int arg_deterministic_exit_code; // always exit with first childs exit status extern int login_shell; extern int parent_to_child_fds[2]; diff --git a/src/firejail/main.c b/src/firejail/main.c index f3dc72944..3e2cc3a17 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -125,6 +125,7 @@ int arg_notv = 0; // --notv int arg_nodvd = 0; // --nodvd int arg_nodbus = 0; // -nodbus int arg_nou2f = 0; // --nou2f +int arg_deterministic_exit_code = 0; // always exit with first childs exit status int login_shell = 0; @@ -2275,6 +2276,9 @@ int main(int argc, char **argv) { return 1; } } + else if (strcmp(argv[i], "--deterministic-exit-code") == 0) { + arg_deterministic_exit_code = 1; + } else { // double dash - positional params to follow if (strcmp(argv[i], "--") == 0) { diff --git a/src/firejail/profile.c b/src/firejail/profile.c index c8619f7e2..55d3cf5b0 100644 --- a/src/firejail/profile.c +++ b/src/firejail/profile.c @@ -1301,6 +1301,11 @@ int profile_check_line(char *ptr, int lineno, const char *fname) { return 0; } + if (strcmp(ptr, "deterministic-exit-code") == 0) { + arg_deterministic_exit_code = 1; + return 0; + } + // rest of filesystem if (strncmp(ptr, "blacklist ", 10) == 0) ptr += 10; diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 9f0a5f25c..c09a1bc73 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -271,6 +271,7 @@ static int monitor_application(pid_t app_pid) { } int status = 0; + int app_status = 0; while (monitored_pid) { usleep(20000); char *msg; @@ -295,6 +296,8 @@ static int monitor_application(pid_t app_pid) { sleep(1); break; } + else if (rv == app_pid) + app_status = status; // handle --timeout if (options) { @@ -352,8 +355,8 @@ static int monitor_application(pid_t app_pid) { printf("Sandbox monitor: monitoring %d\n", monitored_pid); } - // return the latest exit status. - return status; + // return the appropriate exit status. + return arg_deterministic_exit_code ? app_status : status; } static void print_time(void) { diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 7620bba82..ab749413b 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -66,6 +66,7 @@ static char *usage_str = #ifdef HAVE_NETWORK " --defaultgw=address - configure default gateway.\n" #endif + " --deterministic-exit-code - always exit with first childs status code.\n" " --dns=address - set DNS server.\n" " --dns.print=name|pid - print DNS configuration.\n" " --env=name=value - set environment variable.\n" diff --git a/src/man/firejail-profile.txt b/src/man/firejail-profile.txt index 703fac30f..cbc745af7 100644 --- a/src/man/firejail-profile.txt +++ b/src/man/firejail-profile.txt @@ -661,6 +661,10 @@ instead of the default one. Join the sandbox identified by name or start a new one. Same as "firejail --join=sandboxname" command if sandbox with specified name exists, otherwise same as "name sandboxname". +.TP +\fBdeterministic-exit-code +Always exit firejail with the first childs exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic. + .SH FILES /etc/firejail/filename.profile, $HOME/.config/firejail/filename.profile diff --git a/src/man/firejail.txt b/src/man/firejail.txt index e6826448b..fcc7f66d7 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -409,6 +409,10 @@ Example: .br $ firejail \-\-disable-mnt firefox +.TP +\fB\-\-deterministic-exit-code +Always exit firejail with the first childs exit status. The default behavior is to use the exit status of the final child to exit, which can be nondeterministic. + .TP \fB\-\-dns=address Set a DNS server for the sandbox. Up to three DNS servers can be defined. diff --git a/test/environment/deterministic-exit-code.exp b/test/environment/deterministic-exit-code.exp new file mode 100755 index 000000000..165b9ebe0 --- /dev/null +++ b/test/environment/deterministic-exit-code.exp @@ -0,0 +1,55 @@ +#!/usr/bin/expect -f +# This file is part of Firejail project +# Copyright (C) 2014-2019 Firejail Authors +# License GPL v2 + +set timeout 4 +spawn $env(SHELL) +match_max 100000 + +send -- "firejail\r" +expect { + timeout {puts "TESTING ERROR 0\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "({ nohup bash -c \"sleep 0.2; exit 53\" &> /dev/null & } &)\r" +send -- "exit 35\r" +expect { + timeout {puts "TESTING ERROR 1\n";exit} + "Parent is shutting down" +} +after 300 + +send -- "echo $?\r" +expect { + timeout {puts "TESTING ERROR 2\n";exit} + "53" +} +after 100 + +send -- "firejail --deterministic-exit-code\r" +expect { + timeout {puts "TESTING ERROR 3\n";exit} + "Child process initialized" +} +sleep 1 + +send -- "({ nohup bash -c \"sleep 0.2; exit 53\" &> /dev/null & } &)\r" +send -- "exit 35\r" +expect { + timeout {puts "TESTING ERROR 4\n";exit} + "Parent is shutting down" +} +after 300 + +send -- "echo $?\r" +expect { + timeout {puts "TESTING ERROR 5\n";exit} + "35" +} +after 100 + + +puts "\nall done\n" diff --git a/test/environment/environment.sh b/test/environment/environment.sh index 85d6c0873..5b4aa32f4 100755 --- a/test/environment/environment.sh +++ b/test/environment/environment.sh @@ -116,3 +116,6 @@ echo "TESTING: rlimit errors (test/environment/rlimit-bad.exp)" echo "TESTING: rlimit errors profile (test/environment/rlimit-bad-profile.exp)" ./rlimit-bad-profile.exp + +echo "TESTING: deterministic exit code (test/environment/deterministic-exit-code.exp" +./deterministic-exit-code.exp -- cgit v1.2.3-54-g00ecf