Skip to content

Commit a293e6d

Browse files
Detect mutable default identity replay
1 parent e3829c7 commit a293e6d

1 file changed

Lines changed: 67 additions & 0 deletions

File tree

kernelguard.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2202,6 +2202,73 @@ def _has_ver(expr: ast.AST | None) -> bool:
22022202
continue
22032203
if not is_entrypoint_name(node.name):
22042204
continue
2205+
params = {arg.arg for arg in node.args.args}
2206+
2207+
default_state = {
2208+
arg.arg
2209+
for arg, default in zip(node.args.args[-len(node.args.defaults):], node.args.defaults)
2210+
if isinstance(default, (ast.List, ast.Tuple)) and len(default.elts) >= 2
2211+
}
2212+
2213+
for child in ast.walk(node):
2214+
if not isinstance(child, ast.If):
2215+
continue
2216+
if _body_has_calls(child.body):
2217+
continue
2218+
2219+
replay_state = None
2220+
for compare in ast.walk(child.test):
2221+
if not isinstance(compare, ast.Compare):
2222+
continue
2223+
operands = [compare.left] + list(compare.comparators)
2224+
for i, op in enumerate(compare.ops):
2225+
if not isinstance(op, ast.Is):
2226+
continue
2227+
left = operands[i]
2228+
right = operands[i + 1]
2229+
left_root = _ast_root_name(left)
2230+
right_root = _ast_root_name(right)
2231+
if left_root in default_state and right_root in params:
2232+
replay_state = left_root
2233+
elif right_root in default_state and left_root in params:
2234+
replay_state = right_root
2235+
if replay_state is None:
2236+
continue
2237+
2238+
returns_default_slot = any(
2239+
isinstance(stmt, ast.Return)
2240+
and _ast_root_name(stmt.value) == replay_state
2241+
for stmt in child.body
2242+
)
2243+
if not returns_default_slot:
2244+
continue
2245+
2246+
writes_input_slot = False
2247+
writes_output_slot = False
2248+
for stmt in ast.walk(node):
2249+
if not isinstance(stmt, ast.Assign):
2250+
continue
2251+
targets = stmt.targets
2252+
values = [stmt.value] * len(targets)
2253+
if len(targets) == 1 and isinstance(targets[0], ast.Tuple) and isinstance(stmt.value, ast.Tuple):
2254+
targets = list(targets[0].elts)
2255+
values = list(stmt.value.elts)
2256+
for target, value in zip(targets, values):
2257+
if _ast_root_name(target) != replay_state:
2258+
continue
2259+
if isinstance(value, ast.Name) and value.id in params:
2260+
writes_input_slot = True
2261+
elif _expr_names(value) & params:
2262+
writes_output_slot = True
2263+
if writes_input_slot and writes_output_slot:
2264+
return [{
2265+
"pattern": "LAST_CALL_REPLAY",
2266+
"severity": "critical",
2267+
"evidence": (
2268+
f"{entrypoint_name} replays from mutable default state "
2269+
"after input identity match"
2270+
),
2271+
}]
22052272

22062273
signature_features: dict[str, set[str]] = defaultdict(set)
22072274
saved_state_features: dict[str, set[str]] = defaultdict(set)

0 commit comments

Comments
 (0)