From fba8dc14d6f50fc8aab45d25f9cc70519dca1cab Mon Sep 17 00:00:00 2001 From: Sarah Lacard Date: Fri, 1 May 2026 12:18:18 -0600 Subject: [PATCH 1/4] fix(detector): extend THREAD_INJECTION for _thread module KernelGuard-Red-Submission: thread-module-1 --- kernelguard.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/kernelguard.py b/kernelguard.py index f086847..95c929a 100644 --- a/kernelguard.py +++ b/kernelguard.py @@ -2785,6 +2785,33 @@ def detect_dynamic_execution(code: str | SubmissionFacts) -> list[dict]: "severity": "high", "evidence": "importlib.reload() (module state reset/manipulation)", }) + elif func_name == "operator.call" and func_name not in seen: + if node.args and isinstance(node.args[0], ast.Name): + called_name = node.args[0].id + if called_name in ("exec", "eval", "compile"): + seen.add(func_name) + matches.append({ + "pattern": "DYNAMIC_EXECUTION", + "severity": "high", + "evidence": f"operator.call({called_name}, ...) — dynamic execution via operator module", + }) + elif func_name == "functools.partial" and func_name not in seen: + if node.args and isinstance(node.args[0], ast.Name): + called_name = node.args[0].id + if called_name in ("exec", "eval", "compile", "__import__"): + seen.add(func_name) + matches.append({ + "pattern": "DYNAMIC_EXECUTION", + "severity": "high", + "evidence": f"functools.partial({called_name}, ...) — deferred dynamic execution", + }) + elif func_name in ("builtins.exec", "builtins.eval", "builtins.compile") and func_name not in seen: + seen.add(func_name) + matches.append({ + "pattern": "DYNAMIC_EXECUTION", + "severity": "high", + "evidence": f"{func_name}() — dynamic execution via builtins module", + }) return matches From af299116e8f5db641063843b088e9edd22ba1870 Mon Sep 17 00:00:00 2001 From: Sarah Lacard Date: Fri, 1 May 2026 12:18:42 -0600 Subject: [PATCH 2/4] fix(detector): extend DYNAMIC_EXECUTION for operator.call and functools.partial KernelGuard-Red-Submission: operator-exec-1 --- kernelguard.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/kernelguard.py b/kernelguard.py index 95c929a..2a398e0 100644 --- a/kernelguard.py +++ b/kernelguard.py @@ -104,6 +104,7 @@ re.IGNORECASE, ) RE_VERSION_CACHE = re.compile(r'\._version\b') +RE_HASH_CACHE = re.compile(r'\bhash\s*\(\s*\w+\s*\)\s*') RE_CACHE_GET_ASSIGN = re.compile( r'(\w+)\s*=\s*(\w*(?:cache|reuse)\w*)\.get\s*\(', re.IGNORECASE, @@ -3052,6 +3053,35 @@ def detect_precision_downgrade(code: str | SubmissionFacts) -> list[dict]: return matches +def detect_hash_cache_replay(code: str | SubmissionFacts) -> list[dict]: + """Pattern: hash(data) used as cache key for output replay. + + Evades POINTER_REPLAY and LAST_CALL_REPLAY which key on + data_ptr() or id(data). + """ + facts = ensure_submission_facts(code) + active = facts.custom_kernel_active or facts.python_active + entrypoint_name = entrypoint_label(facts.entrypoint_name) + + if not RE_HASH_CACHE.search(active): + return [] + + if not re.search(r'\bh[key]', active) and not re.search(r'\bkey\b.*\bcache', active) and not re.search(r'_cache\s*\[', active): + return [] + + if re.search(r'b""hash', active) or re.search(r"'hash'", active): + return [] + + return [{ + "pattern": "HASH_CACHE_REPLAY", + "severity": "critical", + "evidence": ( + f"{entrypoint_name} uses hash(data) as cache key " + f"for output replay" + ), + }] + + # --------------------------------------------------------------------------- # Score anomaly detection # --------------------------------------------------------------------------- @@ -3577,7 +3607,7 @@ class RulePolicy: AMD_MODULE_MUTATION_FIXTURES, "downgrade", ), "FRAME_WALK_ACCESS": RulePolicy( - "FRAME_WALK_ACCESS", "evaluator_state_support", "telemetry", TELEMETRY_ONLY, + "FRAME_WALK_ACCESS", "evaluator_state_mutation", "hard", AUTO_FILTER, (), (), "split", ), "FRAME_WALK_MUTATION": RulePolicy( @@ -3817,6 +3847,7 @@ def support_only_patterns(matched_patterns: list[dict]) -> bool: detect_thread_injection, detect_lazy_tensor, detect_precision_downgrade, + detect_hash_cache_replay, ] BASE_DETECTOR_SPECS = [ @@ -3854,6 +3885,7 @@ def support_only_patterns(matched_patterns: list[dict]) -> bool: ("thread_injection", detect_thread_injection), ("lazy_tensor", detect_lazy_tensor), ("precision_downgrade", detect_precision_downgrade), + ("hash_cache_replay", detect_hash_cache_replay), ] VALID_RULE_OUTCOMES = {AUTO_FILTER, SUSPICIOUS_ONLY, TELEMETRY_ONLY} From 0d8e5835f20fda9219175a44a56f10aa3efa57a8 Mon Sep 17 00:00:00 2001 From: Sarah Lacard Date: Fri, 1 May 2026 12:19:17 -0600 Subject: [PATCH 3/4] fix(detector): add HASH_CACHE_REPLAY KernelGuard-Red-Submission: hash-cache-1 --- kernelguard.py | 101 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/kernelguard.py b/kernelguard.py index 2a398e0..a78c35d 100644 --- a/kernelguard.py +++ b/kernelguard.py @@ -3082,6 +3082,97 @@ def detect_hash_cache_replay(code: str | SubmissionFacts) -> list[dict]: }] +def _expr_has_getattr_data_ptr(expr: ast.AST | None) -> bool: + """Check if expression contains getattr(x, 'data_ptr')() call.""" + if expr is None: + return False + for node in ast.walk(expr): + if not isinstance(node, ast.Call): + continue + if isinstance(node.func, ast.Name) and node.func.id == "getattr": + if len(node.args) >= 2 and isinstance(node.args[1], ast.Constant) and node.args[1].value == "data_ptr": + return True + return False + + +def detect_getattr_data_ptr_replay(code: str | SubmissionFacts) -> list[dict]: + """Pattern: getattr-based data_ptr access for replay detection. + + Evades AST data_ptr tracking because getattr(data, 'data_ptr')() + uses Name('getattr') + Constant string, not Attribute access. + """ + facts = ensure_submission_facts(code) + tree = facts.ast_tree + if tree is None: + return [] + entrypoint_name = entrypoint_label(facts.entrypoint_name) + + for node in ast.walk(tree): + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + if not is_entrypoint_name(node.name): + continue + + has_getattr_ptr = any( + _expr_has_getattr_data_ptr(child) + for child in ast.walk(node) + ) + if not has_getattr_ptr: + continue + + for child in ast.walk(node): + if not isinstance(child, ast.If): + continue + if _body_has_calls(child.body): + continue + if not isinstance(child.test, ast.BoolOp): + continue + + has_is_none = False + has_getattr_compare = False + + for operand in child.test.values: + if isinstance(operand, ast.Compare): + for op in operand.ops: + if isinstance(op, (ast.Is, ast.IsNot)): + left = operand.left + for right in operand.comparators: + if isinstance(left, ast.Constant) and left.value is None and isinstance(right, ast.Name): + has_is_none = True + elif isinstance(right, ast.Constant) and right.value is None and isinstance(left, ast.Name): + has_is_none = True + break + + if isinstance(operand, ast.Compare): + if has_getattr_ptr and any( + _expr_has_getattr_data_ptr(c) for c in [operand.left] + operand.comparators + ): + has_getattr_compare = True + + if not (has_is_none and has_getattr_compare): + continue + + returned = { + _ast_root_name(stmt.value) + for stmt in child.body + if isinstance(stmt, ast.Return) and stmt.value is not None + } - {None} + if not returned: + continue + if not any(r in facts._none_inited for r in returned): + continue + + return [{ + "pattern": "GETATTR_DATAPTR_REPLAY", + "severity": "critical", + "evidence": ( + f"{entrypoint_name} uses getattr(data, 'data_ptr')() " + f"to evade AST data_ptr tracking" + ), + }] + return [] + + # --------------------------------------------------------------------------- # Score anomaly detection # --------------------------------------------------------------------------- @@ -3702,6 +3793,14 @@ class RulePolicy: "SCORE_EXTREME_SPEEDUP", "score_anomaly", "support", SUSPICIOUS_ONLY, (), (), "downgrade", ), + "HASH_CACHE_REPLAY": RulePolicy( + "HASH_CACHE_REPLAY", "result_reuse", "hard", AUTO_FILTER, (), + (), "keep", + ), + "NOT_EQ_REPLAY": RulePolicy( + "NOT_EQ_REPLAY", "result_reuse", "hard", AUTO_FILTER, (), + (), "keep", + ), "DUPLICATE_SPAM": RulePolicy( "DUPLICATE_SPAM", "administrative", "support", SUSPICIOUS_ONLY, (), (), "downgrade", @@ -4762,7 +4861,7 @@ def _worker_parquet(args: tuple) -> dict: "HARDCODED_SHAPES", "TRIVIAL_PROBE", "OBFUSCATED_EXEC", "DYNAMIC_EXECUTION", "MODULE_RELOAD", "THREAD_INJECTION", "LAZY_TENSOR", "TOKEN_PASTE_CUDA_API", "SEQUENCE_BATCH_GRAPH", "PARTIAL_GRAPH_KEY", "RUNTIME_PACKAGE_INSTALL", - "PRECISION_DOWNGRADE", "SCORE_PHYSICS_FLOOR", "SCORE_IMPOSSIBLE", "SCORE_SUSPECT_FLOOR", + "PRECISION_DOWNGRADE", "HASH_CACHE_REPLAY", "SCORE_PHYSICS_FLOOR", "SCORE_IMPOSSIBLE", "SCORE_SUSPECT_FLOOR", "SCORE_BROKEN", "SCORE_EXTREME_SPEEDUP", "DUPLICATE_SPAM", "NEAR_CLONE_SPAM", ] From 74d52292a0caae0932e90e67fe08329b4f5e0c3b Mon Sep 17 00:00:00 2001 From: Sarah Lacard Date: Fri, 1 May 2026 22:21:17 -0600 Subject: [PATCH 4/4] chore: trigger blue-pr-sync re-evaluation