Skip to content

Commit 8882ee3

Browse files
committed
Add fixture specialization detectors
1 parent babd880 commit 8882ee3

3 files changed

Lines changed: 475 additions & 4 deletions

File tree

kernelguard.py

Lines changed: 156 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2846,6 +2846,149 @@ def detect_hardcoded_shapes(code: str | SubmissionFacts) -> list[dict]:
28462846
return matches
28472847

28482848

2849+
def detect_benchmark_unsafe_algo_dispatch(code: str | SubmissionFacts) -> list[dict]:
2850+
"""Shape/data-family dispatch into unsafe specialized algorithms.
2851+
2852+
Per-shape tuning is legitimate. This detector requires the stronger
2853+
combination: benchmark-shaped branches plus data-family classifiers and
2854+
fast-path terms that imply reduced precision, truncated work, or a generic
2855+
exact fallback for cases outside the public fixture.
2856+
"""
2857+
facts = ensure_submission_facts(code)
2858+
raw_code = facts.raw_code
2859+
active_code = facts.python_active
2860+
combined = f"{active_code}\n{raw_code}"
2861+
low = combined.lower()
2862+
matches = []
2863+
2864+
shape_gate_count = len(re.findall(
2865+
r'(?:\(\s*[Bb]\s*,\s*[Nn]\s*\)\s*==\s*\(\s*\d{1,5}\s*,\s*\d{1,5}\s*\)|'
2866+
r'\b(?:B|b|batch|N|n)\s*(?:==|!=|<=|>=|<|>)\s*\d{1,5})',
2867+
combined,
2868+
))
2869+
benchmark_constant_count = len(re.findall(
2870+
r'\b(?:60|176|352|512|640|768|1024|2048|4096|1280)\b',
2871+
combined,
2872+
))
2873+
family_terms = re.findall(
2874+
r'\b(?:classify_512|classify_1024|detect_label|rankdef|rank_def|clustered|'
2875+
r'nearrank|near_rank|nearcol|near_col|mixed|rowscale|colnorm|zerofrac|'
2876+
r'colrange|tail_max|neardiff|inactive_tail)\b',
2877+
low,
2878+
)
2879+
unsafe_terms = re.findall(
2880+
r'\b(?:zero_tail|trunc|truncate|stop_at|switch_at|ncap|ncols|limit|'
2881+
r'_trunc_stop|tf32|fp16|bf16|fast_path|unsafe|rankdef)\b',
2882+
low,
2883+
)
2884+
fallback_terms = re.findall(
2885+
r'\b(?:torch\.geqrf|torch\.linalg\.(?:qr|lu)|fallback|fall back|exact|generic)\b',
2886+
combined,
2887+
re.IGNORECASE,
2888+
)
2889+
raw_native_cache = (
2890+
bool(re.search(r'\bnvcc\b', low))
2891+
and bool(re.search(r'\b(?:ctypes|CDLL|subprocess\.run|Popen|\.so|\.cu)\b', combined))
2892+
and not bool(re.search(r'\bload_inline\s*\(', active_code))
2893+
and len(re.findall(r'\b(?:data_ptr|ctypes\.c_void_p|uint64_t|void\s*\*)\b', combined)) >= 4
2894+
)
2895+
2896+
strong_shape_fixture = shape_gate_count >= 8 and benchmark_constant_count >= 20
2897+
data_family_routing = len(family_terms) >= 4 and len(unsafe_terms) >= 4
2898+
exact_escape_hatch = len(fallback_terms) >= 2
2899+
2900+
if raw_native_cache and strong_shape_fixture and data_family_routing:
2901+
signals = [
2902+
"raw native compile/load cache",
2903+
f"{shape_gate_count} shape gates",
2904+
f"{len(set(family_terms))} data-family terms",
2905+
f"{len(set(unsafe_terms))} unsafe/truncation terms",
2906+
]
2907+
if exact_escape_hatch:
2908+
signals.append("exact/generic fallback path")
2909+
matches.append({
2910+
"pattern": "BENCHMARK_UNSAFE_ALGO_DISPATCH",
2911+
"severity": "high",
2912+
"evidence": "; ".join(signals),
2913+
})
2914+
2915+
return matches
2916+
2917+
2918+
def detect_input_structure_truncation_dispatch(code: str | SubmissionFacts) -> list[dict]:
2919+
"""Input probes routing to partial computation or exact subset repair.
2920+
2921+
This is not a ban on algorithm selection. It targets classifiers/probes of
2922+
input structure that steer only a detected subset into truncated fast paths,
2923+
tail-zeroing, or reference/exact repair, which is the static shape of a
2924+
public-fixture assumption.
2925+
"""
2926+
facts = ensure_submission_facts(code)
2927+
active_code = facts.python_active
2928+
raw_code = facts.raw_code
2929+
combined = f"{active_code}\n{raw_code}"
2930+
low = combined.lower()
2931+
matches = []
2932+
2933+
probe_terms = re.findall(
2934+
r'\b(?:probe_cond|probe_n|_probe|_probe_conditioning|_s3_collinear|'
2935+
r'spread_thresh|sparse_thresh|corr_thresh|bit0|bit1|classify_512|'
2936+
r'classify_1024|detect_label|colnorm|neardiff|zerofrac|tail_max)\b',
2937+
low,
2938+
)
2939+
subset_terms = re.findall(
2940+
r'\b(?:bad_idx|good_idx|bad_count|good_count|index_select|index_copy|'
2941+
r'scatter_exact|compact_label|panel_labels|label_bad_cache|gather_.*bad|'
2942+
r'compact_.*bad)\b',
2943+
low,
2944+
)
2945+
partial_terms = re.findall(
2946+
r'(?:tau\s*\[[^\]]*limit\s*:\s*\]\s*=\s*0|H\s*\[[^\]]*limit\s*:\s*\]\s*=\s*0|'
2947+
r'\b(?:zero_tail|trunc|truncate|inactive_tail|stop_at|switch_at|ncap|ncols|limit\s*<\s*n)\b)',
2948+
combined,
2949+
re.IGNORECASE,
2950+
)
2951+
repair_terms = re.findall(
2952+
r'\b(?:repair|exact|torch\.geqrf|torch\.linalg\.(?:qr|lu)|index_copy|index_select)\b',
2953+
combined,
2954+
re.IGNORECASE,
2955+
)
2956+
probe_set = set(probe_terms)
2957+
subset_set = set(subset_terms)
2958+
partial_set = set(partial_terms)
2959+
repair_set = set(repair_terms)
2960+
2961+
probe_driven_repair = (
2962+
bool(probe_set & {"probe_cond", "probe_n", "_probe", "_s3_collinear"})
2963+
and len(probe_set) >= 4
2964+
and len(subset_set) >= 3
2965+
and len(repair_set) >= 3
2966+
)
2967+
classifier_driven_partial = (
2968+
bool(probe_set & {"classify_512", "classify_1024", "detect_label"})
2969+
and len(probe_set) >= 4
2970+
and len(partial_set) >= 3
2971+
and len(subset_set) >= 2
2972+
)
2973+
2974+
if probe_driven_repair or classifier_driven_partial:
2975+
signals = [
2976+
f"{len(probe_set)} structure/probe terms",
2977+
f"{len(subset_set)} subset-routing terms",
2978+
]
2979+
if partial_set:
2980+
signals.append(f"{len(partial_set)} partial/truncation terms")
2981+
if repair_set:
2982+
signals.append(f"{len(repair_set)} exact-repair terms")
2983+
matches.append({
2984+
"pattern": "INPUT_STRUCTURE_TRUNCATION_DISPATCH",
2985+
"severity": "high",
2986+
"evidence": "; ".join(signals),
2987+
})
2988+
2989+
return matches
2990+
2991+
28492992
def detect_unsync_multistream(code: str | SubmissionFacts) -> list[dict]:
28502993
"""Pattern 14: Unsynchronized multi-stream dispatch to exploit timing.
28512994
@@ -7289,6 +7432,14 @@ class RulePolicy:
72897432
"HARDCODED_SHAPES", "performance_heuristic", "telemetry", TELEMETRY_ONLY, (),
72907433
(), "downgrade",
72917434
),
7435+
"BENCHMARK_UNSAFE_ALGO_DISPATCH": RulePolicy(
7436+
"BENCHMARK_UNSAFE_ALGO_DISPATCH", "fixture_specialization", "hard", AUTO_FILTER, (),
7437+
(), "keep",
7438+
),
7439+
"INPUT_STRUCTURE_TRUNCATION_DISPATCH": RulePolicy(
7440+
"INPUT_STRUCTURE_TRUNCATION_DISPATCH", "fixture_specialization", "hard", AUTO_FILTER, (),
7441+
(), "keep",
7442+
),
72927443
"UNSYNC_MULTISTREAM": RulePolicy(
72937444
"UNSYNC_MULTISTREAM", "timing_manipulation", "telemetry", TELEMETRY_ONLY, (),
72947445
(), "downgrade",
@@ -7556,6 +7707,8 @@ def support_only_patterns(matched_patterns: list[dict]) -> bool:
75567707
detect_torch_compile_cache,
75577708
detect_cuda_graph_python,
75587709
detect_hardcoded_shapes,
7710+
detect_benchmark_unsafe_algo_dispatch,
7711+
detect_input_structure_truncation_dispatch,
75597712
detect_unsync_multistream,
75607713
detect_cuda_event_disable_timing,
75617714
detect_token_paste_cuda_api,
@@ -7617,6 +7770,8 @@ def support_only_patterns(matched_patterns: list[dict]) -> bool:
76177770
("torch_compile_cache", detect_torch_compile_cache),
76187771
("cuda_graph_python", detect_cuda_graph_python),
76197772
("hardcoded_shapes", detect_hardcoded_shapes),
7773+
("benchmark_unsafe_algo_dispatch", detect_benchmark_unsafe_algo_dispatch),
7774+
("input_structure_truncation_dispatch", detect_input_structure_truncation_dispatch),
76207775
("unsync_multistream", detect_unsync_multistream),
76217776
("cuda_event_disable_timing", detect_cuda_event_disable_timing),
76227777
("token_paste_cuda_api", detect_token_paste_cuda_api),
@@ -8522,7 +8677,7 @@ def _worker_parquet(args: tuple) -> dict:
85228677
"RUNNER_PLAN_CACHE", "CUDA_GRAPH_PYTHON", "CUDA_GRAPH_REPLAY",
85238678
"TIMER_MONKEYPATCH", "FAKE_BENCHMARK_EMIT", "STDIO_REDIRECT", "UNSYNC_MULTISTREAM", "CUDA_EVENT_DISABLE_TIMING",
85248679
"SCALED_MM_REF", "DECODE_MM_REF", "SILENT_FALLBACK", "REFERENCE_PRECOMPUTE_REPLAY", "TORCH_COMPILE_CACHE",
8525-
"HARDCODED_SHAPES", "TRIVIAL_PROBE",
8680+
"HARDCODED_SHAPES", "BENCHMARK_UNSAFE_ALGO_DISPATCH", "INPUT_STRUCTURE_TRUNCATION_DISPATCH", "TRIVIAL_PROBE",
85268681
"OBFUSCATED_EXEC", "DYNAMIC_EXECUTION", "MODULE_RELOAD", "THREAD_INJECTION", "LAZY_TENSOR",
85278682
"TOKEN_PASTE_CUDA_API", "SEQUENCE_BATCH_GRAPH", "PARTIAL_GRAPH_KEY", "RUNTIME_PACKAGE_INSTALL",
85288683
"PRECISION_DOWNGRADE", "SCORE_PHYSICS_FLOOR", "SCORE_IMPOSSIBLE", "SCORE_SUSPECT_FLOOR",

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "kernelguard"
7-
version = "0.3.0"
7+
version = "0.3.1"
88
description = "Rule-based GPU kernel hack detector."
99
readme = "README.md"
1010
requires-python = ">=3.11"

0 commit comments

Comments
 (0)