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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
from pathlib import Path
import os
import resource
import signal
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
def invoke(*args, ok=True, data=None, preexec_fn=None):
result = subprocess.run([str(ROOT / '.build/remy'), *map(str, args)], input=data,
capture_output=True, text=True, timeout=30, preexec_fn=preexec_fn)
assert (result.returncode == 0) == ok, (args, result.stdout, result.stderr)
return result.stdout if ok else result.stderr
def main():
with tempfile.TemporaryDirectory() as directory:
source, binary = Path(directory) / 'source', Path(directory) / 'binary'
for text, expected in [('0x0',0),('0xff',255),('0X7F',127),('0xffffffffffffffff',(1 << 64)-1),
('(+ 0xffffffffffffffff 1)',0),('(< 0x8000000000000000 0xffffffffffffffff)',1)]:
source.write_text(text)
assert int(invoke('eval',source,'0','0','0')) == expected, text
source.write_text('(+ x y)')
assert int(invoke('eval',source,'0xFF','0x1','0x0')) == 256
source.write_text('(let x1 7 (let x1 (+ x1 2) (* x1 x1)))')
assert int(invoke('eval',source,'0','0','0')) == 81
source.write_text('(let 1x 3 1x)')
invoke(source,binary,ok=False)
for text in ['(+ x; ignore ) tokens\ny) ; end', '(+ x; comment\r\ny)', '(+ x y);end']:
source.write_text(text)
assert int(invoke('eval',source,'3','4','0')) == 7, text
for text, position in [('(+ x)',3),('(div x y)',1),('(let a 3 b)',4),('(+ x y',4),('(+ x y) 1',5)]:
source.write_text(text)
assert f'token {position}\n' in invoke(source,binary,ok=False), text
source.write_text('(+ 1 ' * 129 + '0' + ')' * 129)
assert 'parse depth limit' in invoke(source,binary,ok=False)
large = '0'
for _ in range(12):
large = f'(+ {large} {large})'
source.write_text(large)
assert '4096 nodes' in invoke(source,binary,ok=False)
source.write_text('(+ x y)')
invoke('limits',4,3,2,source,binary)
for depth, nodes, stack, message in [(4,2,2,'2 nodes'),(4,3,1,'1 words'),
(0,3,2,'parse depth'),(129,3,2,'compiler bounds'),('bad',3,2,'invalid resource')]:
binary.write_bytes(b'preserve')
assert message in invoke('limits',depth,nodes,stack,source,binary,ok=False)
assert binary.read_bytes() == b'preserve'
source.write_text('(+ x y)')
invoke(source,binary)
listing = invoke('disassemble',binary)
assert 'mov rax r9' in listing and 'arithmetic add' in listing and 'ret' in listing
assert 'push' not in listing and 'pop' not in listing
source.write_text('(- x (- y (- t x)))')
invoke(source,binary)
listing = invoke('disassemble',binary)
assert 'push rax' in listing and 'pop rcx' in listing
source.write_text('(if x (+ y 7) (- t 9))')
invoke(source,binary)
listing = invoke('disassemble',binary)
assert 'jz ' in listing and 'jmp ' in listing and 'conditional ' not in listing
invoke('check',binary)
original = binary.read_bytes()
branch = original.index(bytes.fromhex('48 85 c0 0f 84'))
for offset in (0,4,0x7fffffff,0xffffffff):
damaged = bytearray(original)
damaged[branch+5:branch+9] = offset.to_bytes(4,'little')
binary.write_bytes(damaged)
invoke('check',binary,ok=False)
for cut in range(branch+1,branch+9):
binary.write_bytes(original[:cut]+bytes([195]))
invoke('check',binary,ok=False)
for malformed in [b'',bytes([195,0]),bytes([72,199,192,0]),bytes([80])]:
binary.write_bytes(malformed)
assert 'invalid native' in invoke('disassemble',binary,ok=False)
binary.write_bytes(bytes([72,184,42,0,0,0,0,0,0,0,195]))
assert '10 ret' in invoke('disassemble',binary)
source.write_text('(+ x y)')
invoke(source,binary)
assert 'verified' in invoke('check',binary)
for code in [[88,195],[80,195],[65,88,195],[73,137,192,195],[72,139,68,36,0,195]]:
binary.write_bytes(bytes(code))
invoke('check',binary,ok=False)
binary.write_bytes(bytes([72,199,192,42,0,0,0,195]))
invoke('check',binary)
source.write_text('(+ x y)')
binary.write_bytes(b'preserve')
binary.chmod(0o600)
invoke(source,binary,preexec_fn=lambda: os.umask(0))
assert binary.stat().st_mode & 0o777 == 0o600
def restricted():
signal.signal(signal.SIGXFSZ,signal.SIG_IGN)
resource.setrlimit(resource.RLIMIT_FSIZE,(1,1))
for args in [(source,binary),('vectors',source,binary)]:
binary.write_bytes(b'preserve')
invoke(*args,ok=False,preexec_fn=restricted)
assert binary.read_bytes() == b'preserve'
assert not list(binary.parent.glob('.binary.*'))
target = Path(directory) / 'folder'
target.mkdir()
invoke(source,target,ok=False)
assert not list(target.parent.glob('.folder.*'))
source.write_text('(+ x y)')
invoke(source,source)
assert source.read_bytes().endswith(bytes([195]))
invoke('-',binary,data='(+ x y)')
assert int(invoke('eval','-',3,4,0,data='(+ x y)')) == 7
invoke('vectors','-',binary,data='(+ x y)')
assert binary.stat().st_size == 16384
for data in ['(+ x)', ' ' * 65537]:
binary.write_bytes(b'preserve')
invoke('-',binary,data=data,ok=False)
assert binary.read_bytes() == b'preserve'
source.write_text('é')
assert 'ASCII' in invoke(source,binary,ok=False)
for text in ['0x','0x10000000000000000','0xfg','0xffextra','-0x1']:
source.write_text(text)
binary.write_bytes(b'preserve')
invoke(source,binary,ok=False)
assert binary.read_bytes() == b'preserve', text
print('CLI literal and rejection checks passed')
if __name__ == '__main__':
main()
|