From 1a46419b2f7cd020a9a661b8f6714e6ac40bece4 Mon Sep 17 00:00:00 2001 From: Sinatras Date: Fri, 1 May 2026 21:06:07 +0300 Subject: [PATCH] fix(detector): flag shape-keyed fast-path returning zeros as score-anomaly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Targets red #130 (Score anomaly / physics floor): _seen = set() def custom_kernel(data): k = tuple(data.shape) if k in _seen: return torch.zeros_like(data) _seen.add(k) return data.clone() The kernel 'remembers' shapes in a captured set and fast-paths to torch.zeros_like on already-seen shapes. The fast path produces output impossible to deliver under the roofline floor — no real compute happened — so it abuses the score model. Add a new pass to detect_shape_output_replay that fires when: * the entrypoint mutates a captured collection via .add / .append / .update / .extend (the shape registry) * an If with test ' in ' has a Return whose value is torch.{zeros_like|zeros|empty_like|empty|ones_like|ones}(...) Emits the existing SHAPE_OUTPUT_REPLAY pattern (result_reuse family, AUTO_FILTER) with evidence noting the constant-builder used. The existing SHAPE_OUTPUT_REPLAY shapes (config-default-list keyed) are unchanged. --- kernelguard.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/kernelguard.py b/kernelguard.py index f086847..e025215 100644 --- a/kernelguard.py +++ b/kernelguard.py @@ -1929,6 +1929,11 @@ def detect_reference_precompute_replay(code: str | SubmissionFacts) -> list[dict }] +_TORCH_CONSTANT_TENSOR_BUILDERS = frozenset({ + "zeros_like", "zeros", "empty_like", "empty", "ones_like", "ones", +}) + + def detect_shape_output_replay(code: str | SubmissionFacts) -> list[dict]: """Pattern: output replay keyed only by config / shape state.""" facts = ensure_submission_facts(code) @@ -1937,6 +1942,69 @@ def detect_shape_output_replay(code: str | SubmissionFacts) -> list[dict]: return [] entrypoint_name = entrypoint_label(facts.entrypoint_name) + # Score-anomaly variant: kernel "remembers" shapes in a captured set/dict + # and returns a constant zero / empty tensor on the fast path. Looks + # legitimate at a glance but the early-return path produces output + # impossible at the physics floor (no real compute happened). + # + # _seen = set() + # def custom_kernel(data): + # k = tuple(data.shape) + # if k in _seen: + # return torch.zeros_like(data) + # _seen.add(k) + # return data.clone() + for node in ast.walk(tree): + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) or not is_entrypoint_name(node.name): + continue + # Captured names mutated via attribute method call (.add, .append, + # .update, etc.). These are the shape registries. + mutated_collections: set[str] = set() + for child in ast.walk(node): + if ( + isinstance(child, ast.Call) + and isinstance(child.func, ast.Attribute) + and isinstance(child.func.value, ast.Name) + and child.func.attr in ("add", "append", "update", "extend") + ): + mutated_collections.add(child.func.value.id) + if not mutated_collections: + continue + + for child in ast.walk(node): + if not isinstance(child, ast.If): + continue + test = child.test + if not ( + isinstance(test, ast.Compare) + and len(test.ops) == 1 + and isinstance(test.ops[0], ast.In) + and len(test.comparators) == 1 + and isinstance(test.comparators[0], ast.Name) + and test.comparators[0].id in mutated_collections + ): + continue + for stmt in child.body: + if not isinstance(stmt, ast.Return) or stmt.value is None: + continue + rv = stmt.value + if ( + isinstance(rv, ast.Call) + and isinstance(rv.func, ast.Attribute) + and isinstance(rv.func.value, ast.Name) + and rv.func.value.id == "torch" + and rv.func.attr in _TORCH_CONSTANT_TENSOR_BUILDERS + ): + return [{ + "pattern": "SHAPE_OUTPUT_REPLAY", + "severity": "critical", + "evidence": ( + f"{entrypoint_name} fast-paths to torch.{rv.func.attr}() " + f"on already-seen shape (captured-set membership) — " + f"physics-floor abuse via zero-compute return" + ), + }] + active = facts.python_active if re.search(r'torch\.zeros', active) and re.search(r'return\s+_Z\s*\[\s*key\s*\]', active): return [{