By default, we continue running tests even after a failure, to make diagnostics easier. If a chdir command fails, there is a chance that subsequent chdir commands could accidentally move outside the temporary directory where the test is being run, and then we start messing up with the host's files.
In general, the consequence is to leave garbage files and directories behind. However, some of the files can have side effects (for example a "series" file or a "patches" directory could confuse quilt later), and in theory this could also lead to the corruption or deletion of existing files, which is pretty bad. So add a safety check after every chdir command that we are still inside the test case's working directory. If not, stop the test immediately. Note: this is not about being 100% safe and bullet-proof. The only way to achieve this would be to use chroot or even containers. We still need to trust the test cases to not do anything nasty on purpose. This is only protecting against good test cases going wild on failure. Signed-off-by: Jean Delvare <[email protected]> --- test/run | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) --- quilt.orig/test/run 2019-03-12 10:32:57.867742224 +0100 +++ quilt/test/run 2019-03-12 11:06:56.085457429 +0100 @@ -70,6 +70,7 @@ my $lineno; my $width = ($ENV{COLUMNS} || 80) >> 1; my $origdir = getcwd; my $workdir = defined $ARGV[0] ? "$ARGV[0].$$" : "d.$$"; +my $workdir_absolute; my $keep_workdir = $ENV{KEEP_WORKDIR_IF_FAILED} || 0; sub print_header($) @@ -116,6 +117,11 @@ sub substitute_vars($) return $line; } +sub begins_with($$) +{ + return substr($_[0], 0, length($_[1])) eq $_[1]; +} + sub exec_test($$) { my ($raw_prog, $in) = @_; local (*IN, *IN_DUP, *IN2, *OUT_DUP, *OUT, *OUT2); @@ -130,6 +136,10 @@ sub exec_test($$) { return 1, [ "chdir: $prog->[1]: $!\n" ]; } $ENV{PWD} = getcwd; + # We must always stay inside the test directory + unless (begins_with("$ENV{PWD}/", "$workdir_absolute/")) { + return 1, [ "Illegal chdir $prog->[1]\n", "$ENV{PWD} is not inside $workdir_absolute" ], 1; + } return 0, []; } elsif ($prog->[0] eq "export") { my ($name, $value) = split /=/, $prog->[1]; @@ -220,7 +230,7 @@ sub process_test($$$$) { my ($prog, $prog_line, $in, $out) = @_; print_body "[$prog_line] \$ $prog -- "; - my ($exec_status, $result) = exec_test($prog, $in); + my ($exec_status, $result, $stop) = exec_test($prog, $in); my @good = (); my $good = 1; my $nmax = (@$out > @$result) ? @$out : @$result; @@ -250,7 +260,7 @@ sub process_test($$$$) { } } - return $exec_status; + return $exec_status, $stop; } # Create a dedicated working directory @@ -259,6 +269,7 @@ mkdir $workdir or die "Failed to create $SIG{INT} = sub { if (chdir $origdir) { system "rm -rf $workdir" }; exit 1; }; chdir $workdir or die "Failed to change to directory $workdir: $!"; $ENV{PWD} = getcwd; +$workdir_absolute = $ENV{PWD}; if (defined $ARGV[0]) { open(SOURCE, "$origdir/$ARGV[0]"); @@ -282,9 +293,10 @@ while (defined(my $line = <SOURCE>)) { # We have all input and output, we can execute the command if (defined $prog) { - $last_status = process_test($prog, $prog_line, $in, $out); + my $stop; + ($last_status, $stop) = process_test($prog, $prog_line, $in, $out); $prog = undef; - last if $prog_line >= $opt_l; + last if $prog_line >= $opt_l || $stop; } # Parse the next command -- Jean Delvare SUSE L3 Support _______________________________________________ Quilt-dev mailing list [email protected] https://lists.nongnu.org/mailman/listinfo/quilt-dev
