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
129
130
131
|
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
invoke(source,binary)
assert int(invoke('simulate',binary,'0xFF','0x1','0x0')) == 256
invoke('simulate',binary,'invalid','0','0',ok=False)
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()
|