Skip to content

Commit 7756076

Browse files
committed
refactor: implement subprocess-based path_d safety guard and update triton import handling
1 parent ca1cc7c commit 7756076

13 files changed

Lines changed: 605 additions & 142 deletions

File tree

cppmega_v4/.DS_Store

6 KB
Binary file not shown.

cppmega_v4/_tilelang/_path_d_deps.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@
99

1010
import os
1111
import sys
12+
import importlib
1213
from pathlib import Path
14+
from types import ModuleType
1315

1416
TRITON_FRONTEND_PATH_ENV = "CPPMEGA_MLX_TRITON_FRONTEND_PATH"
1517
FLA_SOURCE_PATH_ENV = "CPPMEGA_MLX_FLA_SOURCE_PATH"
1618
TRITON_FRONTEND_UNSAFE_IMPORT_ENV = "CPPMEGA_V4_ENABLE_UNSAFE_TRITON_IMPORT"
1719

1820
_TRITON_FRONTEND_ROOTS = (
19-
Path("/Users/dave/sources/tilelang"),
2021
Path("/Volumes/external/sources/tilelang"),
22+
Path("/Users/dave/sources/tilelang"),
2123
Path("/private/tmp/tl_poc_review"),
2224
)
2325

@@ -79,6 +81,29 @@ def unsafe_triton_frontend_import_enabled() -> bool:
7981
}
8082

8183

84+
def import_triton_with_local_symbols() -> ModuleType:
85+
"""Import Triton without exporting its LLVM symbols process-wide.
86+
87+
Local Triton builds carry a static LLVM image inside ``libtriton``. On
88+
Darwin, importing that extension with the interpreter's default dlopen
89+
flags can make LLVM symbols visible to later TileLang/TVM loads, which
90+
can abort the process during duplicate LLVM command-line option
91+
registration. ``RTLD_LOCAL`` keeps the native image isolated while still
92+
returning the normal Python module.
93+
"""
94+
95+
if not hasattr(sys, "getdlopenflags") or not hasattr(sys, "setdlopenflags"):
96+
return importlib.import_module("triton")
97+
98+
old_flags = sys.getdlopenflags()
99+
local_flags = getattr(os, "RTLD_NOW", old_flags) | getattr(os, "RTLD_LOCAL", 0)
100+
sys.setdlopenflags(local_flags)
101+
try:
102+
return importlib.import_module("triton")
103+
finally:
104+
sys.setdlopenflags(old_flags)
105+
106+
82107
def unsafe_triton_frontend_import_disabled_reason(root: str | None) -> str:
83108
root_text = f" root={root}" if root else " root=<not found>"
84109
return (
@@ -103,6 +128,7 @@ def unsafe_fla_import_disabled_reason(root: str | None) -> str:
103128
"TRITON_FRONTEND_UNSAFE_IMPORT_ENV",
104129
"ensure_fla_root",
105130
"ensure_triton_frontend_root",
131+
"import_triton_with_local_symbols",
106132
"unsafe_fla_import_disabled_reason",
107133
"unsafe_triton_frontend_import_disabled_reason",
108134
"unsafe_triton_frontend_import_enabled",

cppmega_v4/_tilelang/kda_path_d.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from cppmega_v4._tilelang._path_d_deps import (
2424
ensure_fla_root,
2525
ensure_triton_frontend_root,
26+
import_triton_with_local_symbols,
2627
unsafe_fla_import_disabled_reason,
2728
unsafe_triton_frontend_import_disabled_reason,
2829
unsafe_triton_frontend_import_enabled,
@@ -36,7 +37,7 @@ def _triton_frontend_importable() -> tuple[bool, str]:
3637
if root is None:
3738
return False, "poc.triton_frontend not importable: no local checkout found"
3839
try:
39-
import triton # noqa: F401
40+
import_triton_with_local_symbols()
4041
except Exception as exc:
4142
return False, f"triton not importable: {exc.__class__.__name__}: {exc}"
4243
try:

cppmega_v4/_tilelang/linear_attention_path_d.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from cppmega_v4._tilelang._path_d_deps import (
3232
ensure_fla_root,
3333
ensure_triton_frontend_root,
34+
import_triton_with_local_symbols,
3435
unsafe_fla_import_disabled_reason,
3536
unsafe_triton_frontend_import_disabled_reason,
3637
unsafe_triton_frontend_import_enabled,
@@ -45,7 +46,7 @@ def _triton_frontend_importable() -> tuple[bool, str]:
4546
if root is None:
4647
return False, "poc.triton_frontend not importable: no local checkout found"
4748
try:
48-
import triton # noqa: F401
49+
import_triton_with_local_symbols()
4950
except Exception as exc:
5051
return False, f"triton not importable: {exc.__class__.__name__}: {exc}"
5152
try:

scripts/path_sanity_guard.py

Lines changed: 220 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import argparse
77
import ast
88
import json
9+
import os
10+
import subprocess
11+
import sys
912
from dataclasses import asdict, dataclass
1013
from pathlib import Path
1114
from typing import Any, Iterable, Sequence
@@ -43,6 +46,53 @@
4346
Path("cppmega_v4/_tilelang/linear_attention_path_d.py"): "_fla_chunk_kernel_importable",
4447
Path("cppmega_v4/_tilelang/kda_path_d.py"): "_fla_kda_chunk_importable",
4548
}
49+
PATH_D_UNSAFE_IMPORT_ENV = "CPPMEGA_V4_ENABLE_UNSAFE_TRITON_IMPORT"
50+
PATH_D_DEFAULT_STATUS_TIMEOUT_SECONDS = 20
51+
PATH_D_DEFAULT_STATUS_PROBE_CODE = r"""
52+
import json
53+
import os
54+
import sys
55+
56+
UNSAFE_ENV = "CPPMEGA_V4_ENABLE_UNSAFE_TRITON_IMPORT"
57+
os.environ.pop(UNSAFE_ENV, None)
58+
59+
from cppmega_v4._tilelang.kda_path_d import (
60+
_fla_kda_chunk_importable,
61+
_path_d_runtime_status as kda_runtime_status,
62+
_triton_frontend_importable as kda_triton_importable,
63+
)
64+
from cppmega_v4._tilelang.linear_attention_path_d import (
65+
_fla_chunk_kernel_importable,
66+
_path_d_runtime_status as gdn_runtime_status,
67+
_triton_frontend_importable as gdn_triton_importable,
68+
)
69+
70+
71+
def call(label, fn):
72+
ok, reason = fn()
73+
return {"label": label, "ok": bool(ok), "reason": str(reason)}
74+
75+
76+
payload = {
77+
"probes": [
78+
call("gdn.runtime_status", gdn_runtime_status),
79+
call("gdn.triton_frontend", gdn_triton_importable),
80+
call("gdn.fla_chunk", _fla_chunk_kernel_importable),
81+
call("kda.runtime_status", kda_runtime_status),
82+
call("kda.triton_frontend", kda_triton_importable),
83+
call("kda.fla_chunk", _fla_kda_chunk_importable),
84+
],
85+
"unsafe_modules": sorted(
86+
name
87+
for name in sys.modules
88+
if name == "triton"
89+
or name.startswith("triton.")
90+
or name == "fla"
91+
or name.startswith("fla.")
92+
),
93+
}
94+
print(json.dumps(payload, sort_keys=True))
95+
"""
4696
PATH_E_ADAPTER_MODULES = {
4797
"v4.gdn.path_e": Path("cppmega_v4/nn/_external/mlx_lm_gated_delta_update.py"),
4898
"v4.kda.path_e": Path("cppmega_v4/nn/_external/mlx_lm_kda_update.py"),
@@ -289,6 +339,18 @@ def _first_import_line(function: ast.FunctionDef, module_name: str) -> int | Non
289339
return min(lines) if lines else None
290340

291341

342+
def _first_triton_import_probe_line(function: ast.FunctionDef) -> int | None:
343+
lines = [
344+
line
345+
for line in (
346+
_first_import_line(function, "triton"),
347+
_first_call_line(function, "import_triton_with_local_symbols"),
348+
)
349+
if line is not None
350+
]
351+
return min(lines) if lines else None
352+
353+
292354
def _first_import_prefix_line(function: ast.FunctionDef, module_prefix: str) -> int | None:
293355
lines: list[int] = []
294356
for node in ast.walk(function):
@@ -376,7 +438,7 @@ def _check_v4_path_d_import_guards(repo_root: Path) -> list[Finding]:
376438
function,
377439
"unsafe_triton_frontend_import_enabled",
378440
)
379-
import_line = _first_import_line(function, "triton")
441+
import_line = _first_triton_import_probe_line(function)
380442
if import_line is None:
381443
findings.append(
382444
Finding(
@@ -385,7 +447,7 @@ def _check_v4_path_d_import_guards(repo_root: Path) -> list[Finding]:
385447
location=location,
386448
detail=(
387449
"_triton_frontend_importable() must explicitly probe "
388-
"Triton after the unsafe-import guard"
450+
"Triton with local native symbols after the unsafe-import guard"
389451
),
390452
)
391453
)
@@ -445,6 +507,161 @@ def _check_v4_path_d_import_guards(repo_root: Path) -> list[Finding]:
445507
return findings
446508

447509

510+
def _path_d_disabled_reason_is_clear(reason: str) -> bool:
511+
return (
512+
"unsafe" in reason.lower()
513+
and "runtime adapter not reached" in reason
514+
and PATH_D_UNSAFE_IMPORT_ENV in reason
515+
)
516+
517+
518+
def check_path_d_default_status_no_unsafe_imports(
519+
repo_root: Path = ROOT,
520+
) -> list[Finding]:
521+
"""Run default Path D status probes in a subprocess.
522+
523+
Local Triton/FLA imports can abort the interpreter. This guard keeps that
524+
failure out of the main test process and verifies the default path stays
525+
fail-closed with an actionable disabled reason.
526+
"""
527+
528+
env = os.environ.copy()
529+
env.pop(PATH_D_UNSAFE_IMPORT_ENV, None)
530+
pythonpath = env.get("PYTHONPATH")
531+
env["PYTHONPATH"] = (
532+
f"{repo_root}{os.pathsep}{pythonpath}" if pythonpath else str(repo_root)
533+
)
534+
try:
535+
proc = subprocess.run(
536+
[sys.executable, "-c", PATH_D_DEFAULT_STATUS_PROBE_CODE],
537+
cwd=repo_root,
538+
env=env,
539+
text=True,
540+
capture_output=True,
541+
timeout=PATH_D_DEFAULT_STATUS_TIMEOUT_SECONDS,
542+
check=False,
543+
)
544+
except subprocess.TimeoutExpired:
545+
return [
546+
Finding(
547+
code="path_d_default_status_probe_timeout",
548+
severity="error",
549+
location="v4.path_d",
550+
detail=(
551+
"default Path D status subprocess timed out before proving "
552+
"unsafe Triton/FLA imports stay disabled"
553+
),
554+
)
555+
]
556+
557+
if proc.returncode != 0:
558+
detail = _first_line(proc.stderr or proc.stdout or "no output")
559+
return [
560+
Finding(
561+
code="path_d_default_status_probe_crashed",
562+
severity="error",
563+
location="v4.path_d",
564+
detail=(
565+
"default Path D status subprocess exited with "
566+
f"{proc.returncode}: {detail}"
567+
),
568+
)
569+
]
570+
571+
try:
572+
last_line = next(
573+
line for line in reversed(proc.stdout.splitlines()) if line.strip()
574+
)
575+
payload = json.loads(last_line)
576+
except (StopIteration, json.JSONDecodeError) as exc:
577+
return [
578+
Finding(
579+
code="path_d_default_status_probe_bad_output",
580+
severity="error",
581+
location="v4.path_d",
582+
detail=f"default Path D status subprocess did not emit JSON: {exc}",
583+
)
584+
]
585+
586+
findings: list[Finding] = []
587+
unsafe_modules = payload.get("unsafe_modules")
588+
if not isinstance(unsafe_modules, list):
589+
findings.append(
590+
Finding(
591+
code="path_d_default_status_probe_bad_output",
592+
severity="error",
593+
location="v4.path_d",
594+
detail="default Path D status subprocess omitted unsafe_modules list",
595+
)
596+
)
597+
elif unsafe_modules:
598+
findings.append(
599+
Finding(
600+
code="path_d_default_status_imported_unsafe_deps",
601+
severity="error",
602+
location="v4.path_d",
603+
detail=(
604+
"default Path D status probe imported unsafe modules in "
605+
f"the subprocess: {unsafe_modules[:8]!r}"
606+
),
607+
)
608+
)
609+
610+
probes = payload.get("probes")
611+
if not isinstance(probes, list):
612+
findings.append(
613+
Finding(
614+
code="path_d_default_status_probe_bad_output",
615+
severity="error",
616+
location="v4.path_d",
617+
detail="default Path D status subprocess omitted probes list",
618+
)
619+
)
620+
return findings
621+
622+
for probe in probes:
623+
if not isinstance(probe, dict):
624+
findings.append(
625+
Finding(
626+
code="path_d_default_status_probe_bad_output",
627+
severity="error",
628+
location="v4.path_d",
629+
detail=(
630+
"default Path D status probe payload item is invalid: "
631+
f"{probe!r}"
632+
),
633+
)
634+
)
635+
continue
636+
label = str(probe.get("label") or "unknown")
637+
reason = str(probe.get("reason") or "")
638+
if bool(probe.get("ok")):
639+
findings.append(
640+
Finding(
641+
code="path_d_default_status_marked_available",
642+
severity="error",
643+
location=label,
644+
detail=(
645+
"Path D default status probe reported available "
646+
"without unsafe import opt-in"
647+
),
648+
)
649+
)
650+
if not _path_d_disabled_reason_is_clear(reason):
651+
findings.append(
652+
Finding(
653+
code="path_d_default_status_missing_disabled_reason",
654+
severity="error",
655+
location=label,
656+
detail=(
657+
"Path D default status probe did not return the "
658+
f"unsafe-import disabled reason: {_first_line(reason)}"
659+
),
660+
)
661+
)
662+
return findings
663+
664+
448665
def _check_v4_path_e_adapters(repo_root: Path) -> list[Finding]:
449666
findings: list[Finding] = []
450667
for location, rel_path in PATH_E_ADAPTER_MODULES.items():
@@ -553,6 +770,7 @@ def check_path_contracts(repo_root: Path = ROOT) -> list[Finding]:
553770
)
554771
)
555772
findings.extend(_check_v4_path_d_import_guards(repo_root))
773+
findings.extend(check_path_d_default_status_no_unsafe_imports(repo_root))
556774
findings.extend(_check_v4_path_e_adapters(repo_root))
557775
return findings
558776

tests/test_path_sanity_guard.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,9 @@ def test_declared_path_contracts_cover_m04_and_v4_paths() -> None:
315315
assert [f for f in findings if f.code == "unsafe_path_d_fla_import_probe"] == []
316316
assert [f for f in findings if f.code == "missing_path_e_adapter"] == []
317317
assert [f for f in findings if f.code == "path_e_status_missing_adapter_import"] == []
318+
319+
320+
def test_path_d_default_status_subprocess_guard_fails_closed() -> None:
321+
findings = path_sanity_guard.check_path_d_default_status_no_unsafe_imports(ROOT)
322+
323+
assert findings == []

vbgui/src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ function applySnakeLayout(nodes: Node[], canvasWidth?: number): Node[] {
208208
}
209209
}
210210

211-
let nodeX = x;
211+
let nodeX = direction === 1 ? x : x - curDx;
212212
let nodeY = y;
213213

214214
if (isResidualActiveBranch) {

vbgui/src/components/HFQuickStartModal.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ export function HFQuickStartModal({
264264
[{item.category}] {item.name}
265265
</option>
266266
))}
267+
{!catalog.some(item => item.id === dataset) && (
268+
<option value={dataset}>{dataset}</option>
269+
)}
267270
<option value="custom">-- Custom Dataset ID --</option>
268271
</select>
269272
</label>

0 commit comments

Comments
 (0)