-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparse_log.py
More file actions
271 lines (228 loc) · 9.1 KB
/
parse_log.py
File metadata and controls
271 lines (228 loc) · 9.1 KB
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
https://powcoder.com
代写代考加微信 powcoder
Assignment Project Exam Help
Add WeChat powcoder
from collections import namedtuple, defaultdict
import argparse
import sys
import os.path
parser = argparse.ArgumentParser(description='Grade lab')
parser.add_argument(
'--grade_up_to',
type=int,
default=0,
help='Will grade stages up to this one (included)',
)
parser.add_argument(
'--grade_from', type=int, default=0, help='Will grade stages from this one'
)
parser.add_argument(
'--output', type=str, default='', help='Will write the score to this file'
)
args = parser.parse_args()
PM_PREFIX = 'PM_DUMP'
VM_PREFIX = 'VM_DUMP'
PANIC_PREFIX = 'PANIC: Unexpected exception 52!'
# Per-page state for a page in physical and virtual memory.
PMEntry = namedtuple('PMEntry', ['owner', 'refcount'])
VMEntry = namedtuple('VMEntry', ['owner', 'refcount', 'ua'])
# A bunch of constants.
PAGE_SIZE = 4096
KERNEL_LIMIT = 0x100000 // PAGE_SIZE
CGA_BLOCK = 0xB8000 // PAGE_SIZE
# Offset into the start of kernel memory that should not be initially
# owned by processes.
PHYSICAL_UNALLOCATED_OFFSET = 17
class BColors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class MemMapInstance:
def __init__(self):
self.pm_state = None
self.vm_state = {}
def set_pm_state(self, pm_state):
assert self.pm_state == None
self.pm_state = pm_state
def add_vm_state(self, process_id, vm_state):
assert not process_id in self.vm_state
self.vm_state[process_id] = vm_state
def IsKernelIsolated(self):
# Returns true if all of kernel's addresses are not accessible
# from any of the processes (except for the CGA block).
for process_id, entries in list(self.vm_state.items()):
for i, entry in enumerate(entries):
if i >= KERNEL_LIMIT:
break
if i != CGA_BLOCK and entry.ua:
return False
return True
def AreProcessesIsolated(self):
for process_id, entries in list(self.vm_state.items()):
for i, entry in enumerate(entries):
if i < KERNEL_LIMIT:
# In kernel mem, checked by IsKernelIsolated
continue
if entry.owner and entry.owner != process_id and entry.ua:
return False
return True
def IsVPAUsed(self):
# Will check if virtual page allocation is used. Will simply
# look at the owners of physical pages and make sure that at
# least one physical page outside the address range of any
# process is owned by a process.
offset = PHYSICAL_UNALLOCATED_OFFSET
for i, entry in enumerate(self.pm_state):
if i < offset:
continue
if i >= KERNEL_LIMIT:
return False
if entry.owner and entry.owner <= 4:
return True
# Unreachable
return False
def AreProcessAddressesOverlapping(self):
# Will check if there is at least one process that occupies
# more than 64 pages of virtual memory.
occupancy = defaultdict(int)
for process_id, entries in list(self.vm_state.items()):
for i, entry in enumerate(entries):
if i < KERNEL_LIMIT:
# In kernel mem.
continue
if entry.owner and entry.owner == process_id and entry.ua:
occupancy[process_id] += 1
return any(v > 64 for _, v in list(occupancy.items()))
def CheckOwnershipInvariant(self):
# Checks that all pages in the virtual address space of the
# process are accessible by the process if they are owned by
# it.
for process_id, entries in list(self.vm_state.items()):
for i, entry in enumerate(entries):
if i < KERNEL_LIMIT:
# In kernel mem.
continue
if entry.owner and entry.owner == process_id and not entry.ua:
return False
return True
def CheckCGAAccessInvariant(self):
# The CGA block should be accessible by all processes.
for process_id, entries in list(self.vm_state.items()):
if not entries[CGA_BLOCK].ua:
return False
return True
def IsForkOK(self):
# Checks to see if all processes' virtual address space starts
# at the same place.
for process_id, entries in list(self.vm_state.items()):
entry = entries[KERNEL_LIMIT]
if not entry.owner or entry.owner != process_id:
return False
return True
class MemMapTimeline:
def __init__(self, log_file):
# Parsed data. For each tick will store an instance of
# MemMapInstance.
self.mem_data = defaultdict(MemMapInstance)
self.panic = False
with open(log_file) as f:
lines = f.readlines()
for line in lines:
if line.startswith(PM_PREFIX):
line_split = list(filter(bool, line.split(' ')))
# The label, the tick 'timestamp' and the newline at
# the end, 512 pages in physical memory.
assert len(line_split) - 3 == 512 * 2
tick = int(line_split[1])
data = []
for i in range(512):
owner, refcount = line_split[2 * i + 2 : 2 * i + 4]
data.append(PMEntry(int(owner), int(refcount)))
self.mem_data[tick].set_pm_state(data)
elif line.startswith(VM_PREFIX):
line_split = list(filter(bool, line.split(' ')))
# The label, the id, the tick 'timestamp' and the
# newline at the end, 768 pages in virtual memory.
assert len(line_split) - 4 == 768 * 3
process_id = int(line_split[1])
tick = int(line_split[2])
data = []
for i in range(768):
owner, refcount, ua = line_split[3 * i + 3 : 3 * i + 6]
data.append(VMEntry(int(owner), int(refcount), int(ua)))
self.mem_data[tick].add_vm_state(process_id, data)
elif line.startswith(PANIC_PREFIX):
self.panic = True
def PerformCheck(self, check_function, message, start_at_tick):
res = 0
tick = 0
for tick, instance in sorted(self.mem_data.items()):
if tick < start_at_tick:
continue
func = getattr(instance, check_function)
if func():
res = 1
else:
return (0, tick)
return (res, tick)
def PerformChecks(self, grade_from, up_to):
all_functions = [
('IsKernelIsolated', 'Kernel isolation', 0),
('AreProcessesIsolated', 'Process isolation', 0),
('IsVPAUsed', 'Virtual page allocation', 500),
('AreProcessAddressesOverlapping', 'Overlapping address spaces', 800),
('IsForkOK', 'Fork', 800),
]
to_perform = all_functions[grade_from:up_to]
total = 0
for function_name, message, start_at_tick in to_perform:
res, tick = self.PerformCheck(function_name, message, start_at_tick)
if res:
print('{}{} passed{}'.format(BColors.OKGREEN, message, BColors.ENDC))
else:
print(
'{}{} failed at tick {}{}'.format(
BColors.FAIL, message, tick, BColors.ENDC
)
)
total += res
if args.output != '':
with open(args.output, 'w') as f:
f.write(str(total))
else:
print('Total score: {}/5'.format(total))
def CheckInvariants(self, expected_max_tick):
if self.mem_data:
max_tick = max(self.mem_data.keys())
if not self.panic and expected_max_tick != max_tick:
print(
'Kernel prematurely sys.exited, expected to last until tick',
expected_max_tick,
'but last recorded tick is',
max_tick,
)
sys.exit(1)
for tick, instance in sorted(self.mem_data.items()):
if not instance.CheckOwnershipInvariant():
print(
'At least one non-kernel page owned by a process is not accessible by it at tick ',
tick,
)
sys.exit(1)
if not instance.CheckCGAAccessInvariant():
print(
'At least one process does not have access to the CGA console at tick',
tick,
)
sys.exit(1)
if not os.path.isfile('log.txt'):
print('No output from run, did code compile?')
sys.exit(1)
timeline = MemMapTimeline('log.txt')
timeline.CheckInvariants(900)
timeline.PerformChecks(args.grade_from, args.grade_up_to)