Skip to content

Commit 57d3c74

Browse files
fix(detector): detect direct self matmul output
1 parent e022b96 commit 57d3c74

1 file changed

Lines changed: 62 additions & 10 deletions

File tree

‎kernelguard.py‎

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -591,14 +591,41 @@ def _is_param_t_call(expr: ast.AST | None, param: str) -> bool:
591591
)
592592

593593

594-
def _is_self_matmul_expr(expr: ast.AST | None, params: set[str]) -> bool:
594+
def _is_self_matmul_expr(
595+
expr: ast.AST | None,
596+
params: set[str],
597+
torch_aliases: Optional[set[str]] = None,
598+
mm_aliases: Optional[set[str]] = None,
599+
) -> bool:
600+
torch_aliases = torch_aliases or set()
601+
mm_aliases = mm_aliases or set()
595602
if isinstance(expr, ast.BinOp) and isinstance(expr.op, ast.MatMult):
596603
return any(
597604
isinstance(expr.left, ast.Name)
598605
and expr.left.id == param
599606
and _is_param_t_call(expr.right, param)
600607
for param in params
601608
)
609+
if not isinstance(expr, ast.Call):
610+
return False
611+
if len(expr.args) != 2 or expr.keywords:
612+
return False
613+
for param in params:
614+
if not (
615+
isinstance(expr.args[0], ast.Name)
616+
and expr.args[0].id == param
617+
and _is_param_t_call(expr.args[1], param)
618+
):
619+
continue
620+
if isinstance(expr.func, ast.Name) and expr.func.id in mm_aliases:
621+
return True
622+
if (
623+
isinstance(expr.func, ast.Attribute)
624+
and expr.func.attr in {"mm", "matmul"}
625+
and isinstance(expr.func.value, ast.Name)
626+
and expr.func.value.id in torch_aliases
627+
):
628+
return True
602629
return False
603630

604631

@@ -713,7 +740,12 @@ def _input_reduction_return_from_body(
713740
return saw_return
714741

715742

716-
def _self_matmul_return_from_body(body: list[ast.stmt], params: set[str]) -> bool:
743+
def _self_matmul_return_from_body(
744+
body: list[ast.stmt],
745+
params: set[str],
746+
torch_aliases: Optional[set[str]] = None,
747+
mm_aliases: Optional[set[str]] = None,
748+
) -> bool:
717749
aliases: set[str] = set()
718750
saw_return = False
719751

@@ -730,12 +762,12 @@ def _self_matmul_return_from_body(body: list[ast.stmt], params: set[str]) -> boo
730762
isinstance(stmt, ast.Assign)
731763
and len(stmt.targets) == 1
732764
and isinstance(stmt.targets[0], ast.Name)
733-
and _is_self_matmul_expr(stmt.value, params)
765+
and _is_self_matmul_expr(stmt.value, params, torch_aliases, mm_aliases)
734766
):
735767
aliases.add(stmt.targets[0].id)
736768
continue
737769
if isinstance(stmt, ast.Return):
738-
if _is_self_matmul_expr(stmt.value, params):
770+
if _is_self_matmul_expr(stmt.value, params, torch_aliases, mm_aliases):
739771
saw_return = True
740772
continue
741773
if isinstance(stmt.value, ast.Name) and stmt.value.id in aliases:
@@ -1658,25 +1690,45 @@ def detect_partial_self_matmul_output(code: str | SubmissionFacts) -> list[dict]
16581690
helper_names: set[str] = set()
16591691
partial_aliases = {"partial"}
16601692
functools_aliases = {"functools"}
1693+
torch_aliases = {"torch"}
1694+
mm_aliases: set[str] = set()
16611695

16621696
for node in facts._imports:
16631697
for alias in node.names:
16641698
if alias.name == "functools":
16651699
functools_aliases.add(alias.asname or alias.name)
1700+
if alias.name == "torch":
1701+
torch_aliases.add(alias.asname or alias.name)
16661702
for node in facts._import_froms:
1667-
if node.module != "functools":
1668-
continue
1669-
for alias in node.names:
1670-
if alias.name == "partial":
1671-
partial_aliases.add(alias.asname or alias.name)
1703+
if node.module == "functools":
1704+
for alias in node.names:
1705+
if alias.name == "partial":
1706+
partial_aliases.add(alias.asname or alias.name)
1707+
if node.module == "torch":
1708+
for alias in node.names:
1709+
if alias.name in {"mm", "matmul"}:
1710+
mm_aliases.add(alias.asname or alias.name)
16721711

16731712
for node in ast.walk(tree):
16741713
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
16751714
continue
16761715
params = {arg.arg for arg in node.args.args}
1677-
if params and _self_matmul_return_from_body(node.body, params):
1716+
if params and _self_matmul_return_from_body(node.body, params, torch_aliases, mm_aliases):
16781717
helper_names.add(node.name)
16791718

1719+
for node in ast.walk(tree):
1720+
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
1721+
continue
1722+
if not is_entrypoint_name(node.name):
1723+
continue
1724+
params = {arg.arg for arg in node.args.args}
1725+
if params and _self_matmul_return_from_body(node.body, params, torch_aliases, mm_aliases):
1726+
return [{
1727+
"pattern": "SELF_MATMUL_OUTPUT",
1728+
"severity": "critical",
1729+
"evidence": "custom_kernel returns data @ data.t() or equivalent self-matmul",
1730+
}]
1731+
16801732
for stmt in tree.body:
16811733
if not isinstance(stmt, ast.Assign):
16821734
continue

0 commit comments

Comments
 (0)