1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
#!/usr/bin/env python3
# Copyright Catch2 Authors
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE.txt or copy at
# https://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
"""
Checks the premature-exit guard file: the runner creates it when the run
starts and removes it when the run ends. A test that ends its process is a
crash the runner reports, so the guard is removed then too. The guard is
created only the user can read, and a symbolic link in its place is refused.
Arguments: the test binary and a directory for the guard files.
"""
import os
import subprocess
import sys
def guard_path(directory: str) -> str:
return os.path.join(directory, 'guard_file.' + os.urandom(8).hex())
def run(cmd, env = None, expected = 0):
print('Running:', cmd)
result = subprocess.run(cmd,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
universal_newlines = True,
env = env)
if result.returncode != expected:
print('Return code {} instead of {}'.format(result.returncode, expected))
print('stdout:', result.stdout)
print('stderr:', result.stderr)
exit(1)
return result
bin_path = os.path.abspath(sys.argv[1])
output_dir = os.path.abspath(sys.argv[2])
# Bazel names the guard in the environment.
path = guard_path(output_dir)
env = os.environ.copy()
env['BAZEL_TEST'] = '1'
env['TEST_PREMATURE_EXIT_FILE'] = path
run([bin_path, 'pass'], env)
assert not os.path.exists(path), 'the guard was left behind after a normal run'
# A test that exits the process is a crash; the run still ends normally.
path = guard_path(output_dir)
run([bin_path, '--retries=off', '--premature-exit-guard-file=' + path], expected = 42)
assert not os.path.exists(path), 'the guard was left behind after a crash'
if os.name != 'nt':
# A stale guard with loose permissions is tightened while the run lasts.
path = guard_path(output_dir)
with open(path, 'w'):
pass
os.chmod(path, 0o666)
run([bin_path, 'pass', '--premature-exit-guard-file=' + path])
target_path = path + '.target'
symlink_path = path + '.symlink'
sentinel = 'do not truncate a symlink target\n'
with open(target_path, 'w') as target:
target.write(sentinel)
os.symlink(target_path, symlink_path)
try:
result = subprocess.run(
[bin_path, 'pass', '--premature-exit-guard-file=' + symlink_path],
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
universal_newlines = True)
assert result.returncode != 0, 'a symlink was accepted as the guard file'
with open(target_path, 'r') as target:
assert target.read() == sentinel, 'the symlink target was modified'
finally:
os.remove(symlink_path)
os.remove(target_path)
|