Skip to content

Commit fb7fd9e

Browse files
Jerome CosteCopilot
authored andcommitted
trace : recover hosted correctness blockers
Copilot-Session: c3ea1bf8-f288-47b4-9e1b-3435f2917bc1 Assisted-by: GPT-5.6 Sol Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 924ad46 commit fb7fd9e

10 files changed

Lines changed: 161 additions & 12 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ option(LLAMA_BUILD_COMMON "llama: build common utils library" ${LLAMA_STANDALONE
131131
# extra artifacts
132132
option(LLAMA_BUILD_TESTS "llama: build tests" ${LLAMA_STANDALONE})
133133
option(LLAMA_BUILD_TOOLS "llama: build tools" ${LLAMA_STANDALONE})
134+
option(LLAMA_DEEPSEEK_V41_NATIVE_CONTAINMENT_TESTS "llama: run native Linux DeepSeek V4.1 containment tests" OFF)
134135
option(LLAMA_BUILD_EXAMPLES "llama: build examples" ${LLAMA_STANDALONE})
135136
option(LLAMA_BUILD_SERVER "llama: build server example" ${LLAMA_STANDALONE})
136137
option(LLAMA_BUILD_APP "llama: build the unified binary" ${LLAMA_STANDALONE})

tests/CMakeLists.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ if (NOT WIN32 OR NOT BUILD_SHARED_LIBS)
203203
llama_build_and_test(test-deepseek41-memory.cpp)
204204
llama_build_and_test(test-deepseek41-runtime.cpp)
205205
llama_build_and_test(test-engram.cpp)
206+
llama_build_and_test(test-server-child-protocol.cpp)
207+
target_include_directories(test-server-child-protocol PRIVATE ${PROJECT_SOURCE_DIR}/tools/server)
206208
llama_build(test-llama-archs.cpp)
207209
llama_test(
208210
test-llama-archs
@@ -225,9 +227,17 @@ if (NOT WIN32 OR NOT BUILD_SHARED_LIBS)
225227
TEST test-deepseek41-trace
226228
APPEND PROPERTY ENVIRONMENT
227229
"DSV41_NATIVE_TRACE_BINARY=$<TARGET_FILE:llama-deepseek-v41-trace>"
228-
"DSV41_NATIVE_CONTAINMENT_HELPER=$<TARGET_FILE:llama-deepseek-v41-containment-helper>"
229230
"DSV41_NATIVE_MANIFEST_BINARY=$<TARGET_FILE:test-deepseek41-trace-manifest>"
230231
"DSV41_NATIVE_INJECT_LIBRARY=$<TARGET_FILE:test-deepseek41-trace-injected>")
232+
if(LLAMA_DEEPSEEK_V41_NATIVE_CONTAINMENT_TESTS)
233+
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
234+
message(FATAL_ERROR "Native DeepSeek V4.1 containment tests require Linux")
235+
endif()
236+
set_property(
237+
TEST test-deepseek41-trace
238+
APPEND PROPERTY ENVIRONMENT
239+
"DSV41_NATIVE_CONTAINMENT_HELPER=$<TARGET_FILE:llama-deepseek-v41-containment-helper>")
240+
endif()
231241
endif()
232242
endif()
233243

tests/test-deepseek41-trace.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,15 @@ def finish_native_test_supervisor(supervisor, *, kill_if_running):
258258
"runtime_libraries": DS4_RUNTIME_LIBRARIES,
259259
"runtime_libraries_post": copy.deepcopy(DS4_RUNTIME_LIBRARIES),
260260
}
261+
DS4_CONTAINMENT_HELPER = {
262+
"format": "dsv41-containment-helper",
263+
"version": 2,
264+
"revision": trace.DS4_REVISION,
265+
"filename": "llama-deepseek-v41-containment-helper",
266+
"sha256": "d" * 64,
267+
"launcher_policy": "zero-supplementary-groups-v1",
268+
"supplementary_groups": [],
269+
}
261270
DS4_INSTALL_TRUST = {
262271
"format": "dsv41-install-trust",
263272
"version": 1,
@@ -303,6 +312,10 @@ def finish_native_test_supervisor(supervisor, *, kill_if_running):
303312
}
304313
for index, (path, digest) in enumerate(
305314
sorted((
315+
(
316+
"/Users/oracle/ds4-install/bin/llama-deepseek-v41-containment-helper",
317+
DS4_CONTAINMENT_HELPER["sha256"],
318+
),
306319
("/Users/oracle/ds4-install/bin/ds4-trace", FIXTURE_DS4_EXPORTER_SHA256),
307320
("/Users/oracle/ds4-install/lib/libds4-runtime.dylib", "a" * 64),
308321
("/Users/oracle/ds4-install/lib/libds4-metal.dylib", "b" * 64),
@@ -319,6 +332,7 @@ def finish_native_test_supervisor(supervisor, *, kill_if_running):
319332
"install_owner_uid": 0,
320333
"executable_path": "/Users/oracle/ds4-install/bin/ds4-trace",
321334
"executable_sha256": FIXTURE_DS4_EXPORTER_SHA256,
335+
"containment_helper": DS4_CONTAINMENT_HELPER,
322336
"runtime_profile": DS4_RUNTIME_PROFILE,
323337
"runtime_receipt": DS4_RUNTIME_RECEIPT,
324338
}
@@ -2056,6 +2070,14 @@ def test_ds4_approval_rejects_runtime_identity_mismatches(self) -> None:
20562070
lambda value: value["runtime_receipt"].update({"profile": "co-located"}),
20572071
"runtime receipt identity",
20582072
),
2073+
(
2074+
lambda value: value.pop("containment_helper"),
2075+
"missing containment_helper",
2076+
),
2077+
(
2078+
lambda value: value["containment_helper"].update({"revision": "a" * 40}),
2079+
"containment helper receipt",
2080+
),
20592081
(
20602082
lambda value: [
20612083
component.update({"revision": None})
@@ -2203,11 +2225,16 @@ def test_approved_executable_postchecks_before_strict_decode(self) -> None:
22032225
executable.parent.mkdir(parents=True)
22042226
executable.write_bytes(b"approved")
22052227
executable.chmod(0o555)
2228+
revision = "a" * 40
22062229
policy = {
2230+
"revision": revision,
22072231
"install_root": str(install),
22082232
"install_owner_uid": os.geteuid() if hasattr(os, "geteuid") else 0,
2233+
"executable_path": str(executable),
2234+
"containment_helper": fixture_containment_helper(revision),
22092235
"runtime_receipt": {"components": []},
22102236
}
2237+
materialize_policy_runtime(policy)
22112238
events = []
22122239
completed = subprocess.CompletedProcess([str(executable)], 0, b"\xff", b"")
22132240
contained = trace._ContainedRun(completed, None, [], None, True, True)
@@ -4176,11 +4203,16 @@ def test_containment_handle_close_failure_forces_quiescence_false(self) -> None:
41764203
executable.parent.mkdir(parents=True)
41774204
executable.write_bytes(b"approved")
41784205
executable.chmod(0o555)
4206+
revision = "a" * 40
41794207
policy = {
4208+
"revision": revision,
41804209
"install_root": str(install),
41814210
"install_owner_uid": os.geteuid() if hasattr(os, "geteuid") else 0,
4211+
"executable_path": str(executable),
4212+
"containment_helper": fixture_containment_helper(revision),
41824213
"runtime_receipt": {"components": []},
41834214
}
4215+
materialize_policy_runtime(policy)
41844216
result = subprocess.CompletedProcess([str(executable)], 0, b"", b"")
41854217
contained = trace._ContainedRun(result, None, [], mock.Mock(), True, True)
41864218
close_failure = trace._IntegrityFailure(
@@ -4336,6 +4368,43 @@ def test_ds4_policy_and_receipt_mutation_change_approval_identity(self) -> None:
43364368
seen_run_ids=None,
43374369
)
43384370

4371+
def test_ds4_install_trust_producer_binds_containment_helper(self) -> None:
4372+
with tempfile.TemporaryDirectory() as temp:
4373+
policy, exporter = materialize_ds4_exporter_policy(Path(temp).resolve())
4374+
with isolated_test_install_trust():
4375+
exporter_identity = run_ds4.approved_executable_identity(
4376+
exporter,
4377+
install_root=policy["install_root"],
4378+
expected_owner_uid=policy["install_owner_uid"],
4379+
expected_path=policy["executable_path"],
4380+
expected_sha256=policy["executable_sha256"],
4381+
label="ds4 exporter",
4382+
)
4383+
runtime_identities = run_ds4.approved_runtime_file_identities(
4384+
policy, label="ds4 exporter")
4385+
trust = run_ds4.exporter_install_trust_evidence(
4386+
exporter_identity, runtime_identities, policy)
4387+
self.assertEqual(
4388+
trace.validate_install_trust_evidence(trust, policy),
4389+
trust,
4390+
)
4391+
self.assertIn(
4392+
str(Path(policy["install_root"]) / "bin" / policy["containment_helper"]["filename"]),
4393+
{item["path"] for item in trust["files"]},
4394+
)
4395+
4396+
omitted = trace.install_trust_evidence(
4397+
exporter_identity, runtime_identities)
4398+
with self.assertRaisesRegex(
4399+
trace.TraceError, "files differ from external approval"):
4400+
trace.validate_install_trust_evidence(omitted, policy)
4401+
4402+
wrong_helper = copy.deepcopy(policy)
4403+
wrong_helper["containment_helper"]["sha256"] = "e" * 64
4404+
with self.assertRaisesRegex(run_ds4.TraceError, "SHA-256 differs"):
4405+
run_ds4.exporter_install_trust_evidence(
4406+
exporter_identity, runtime_identities, wrong_helper)
4407+
43394408
def test_install_trust_evidence_rejects_mutability_claims(self) -> None:
43404409
policy = fixture_prompt_builder_policy(b"prompt")
43414410
mutations = {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include "server-child-protocol.h"
2+
3+
#include <cassert>
4+
#include <string>
5+
#include <string_view>
6+
7+
int main() {
8+
constexpr std::string_view state =
9+
"cmd_child_to_router:state:{\"state\":\"downloading\",\"payload\":{\"result\":\"download_finished\"}}\n";
10+
11+
const std::string reset_state = std::string("\x1b[0m") + std::string(state);
12+
const std::string repeated_reset_state = std::string("\x1b[0m\x1b[0m") + std::string(state);
13+
const std::string colored_state = std::string("\x1b[31m") + std::string(state);
14+
15+
assert(server_child_state_line(state) == state);
16+
assert(server_child_state_line(reset_state) == state);
17+
assert(server_child_state_line(repeated_reset_state) == state);
18+
assert(server_child_state_line(colored_state).empty());
19+
assert(server_child_state_line("ordinary child log\n").empty());
20+
assert(server_child_state_line("\x1b[0cmd_child_to_router:state:{}\n").empty());
21+
return 0;
22+
}

tools/deepseek-v41-trace/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ build-dsv41-trace-rocm/bin/test-backend-ops -b ROCm0 -o SET_ROWS
118118
build-dsv41-trace-rocm/bin/test-backend-ops -b ROCm0 -o CPY
119119
```
120120

121+
Native Linux containment tests are not enabled by default because generic hosted runners do not provide the required mount policy. A dedicated single-purpose runner must set `-DLLAMA_DEEPSEEK_V41_NATIVE_CONTAINMENT_TESTS=ON` and use exactly one administrator-provisioned host configuration: `kernel.apparmor_restrict_unprivileged_userns=0`, or a narrowly scoped AppArmor allow policy for the unchanged receipt-bound helper user namespace, `MS_PRIVATE`, private procfs, and verification sequence. Exact AppArmor policy syntax is host-specific and is not supplied here. The test and production launcher still fail closed when the required namespace operations are unavailable.
122+
121123
Do not change host ROCm packages for this run. Vulkan can provide secondary coverage, but it cannot replace the required ROCm low-level and oracle evidence. The llama runner selects `ROCm0` explicitly, invokes the exact exporter for a pre-allocation device attestation, and rejects the run unless the backend PCI identity maps to exactly one KFD node reporting `gfx1151`. The native exporter repeats the query before model allocation and verifies that the loaded model still uses the same device.
122124

123125
Static repository builds skip this shared-library trace component instead of failing configuration.

tools/deepseek-v41-trace/run_ds4.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
TraceError,
3939
TraceVerifier,
4040
approval_binding,
41+
approved_containment_helper_identity,
4142
approved_executable_identity,
4243
approved_runtime_file_identities,
4344
bind_execution_authorization,
@@ -84,6 +85,16 @@ def __init__(
8485
self.secondary_errors = tuple(secondary_errors)
8586

8687

88+
def exporter_install_trust_evidence(
89+
exporter_identity: ExecutableFileReceipt,
90+
runtime_identities: list[ExecutableFileReceipt],
91+
exporter_policy: dict[str, Any]) -> dict[str, Any]:
92+
helper_identity = approved_containment_helper_identity(
93+
exporter_policy, label="ds4 exporter")
94+
return install_trust_evidence(
95+
exporter_identity, runtime_identities, (helper_identity,))
96+
97+
8798
def git_output(checkout: Path, *args: str) -> str:
8899
try:
89100
return subprocess.check_output(
@@ -593,8 +604,8 @@ def main() -> int:
593604
)
594605
runtime_identities = approved_runtime_file_identities(
595606
exporter_policy, label="ds4 exporter")
596-
exporter_install_trust = install_trust_evidence(
597-
exporter_identity, runtime_identities)
607+
exporter_install_trust = exporter_install_trust_evidence(
608+
exporter_identity, runtime_identities, exporter_policy)
598609
exporter_install_trust_sha256 = install_trust_sha256(exporter_install_trust)
599610
pre_runtime_build = query_runtime_build_attestation(
600611
exporter,

tools/deepseek-v41-trace/trace_format.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2668,6 +2668,7 @@ def ds4_exporter_approval(
26682668
"install_owner_uid",
26692669
"executable_path",
26702670
"executable_sha256",
2671+
"containment_helper",
26712672
"runtime_profile",
26722673
"runtime_receipt",
26732674
},
@@ -2686,6 +2687,12 @@ def ds4_exporter_approval(
26862687
executable = PurePosixPath(executable_path)
26872688
if executable.parent != PurePosixPath(install_root) / "bin":
26882689
raise TraceError("ds4 exporter approval executable path is outside its install policy")
2690+
_validate_containment_helper_policy(
2691+
policy["containment_helper"],
2692+
install_root=install_root,
2693+
revision=policy["revision"],
2694+
label="ds4 exporter approval",
2695+
)
26892696
profile = policy["runtime_profile"]
26902697
if not isinstance(profile, dict):
26912698
raise TraceError("ds4 exporter approval runtime profile is invalid")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
#include <string_view>
4+
5+
inline constexpr std::string_view SERVER_CHILD_STATE_PREFIX = "cmd_child_to_router:state:";
6+
7+
inline std::string_view server_child_state_line(std::string_view line) {
8+
constexpr std::string_view ansi_reset = "\x1b[0m";
9+
10+
// Colored logs write the reset after the newline, before the next raw protocol line.
11+
while (line.compare(0, ansi_reset.size(), ansi_reset) == 0) {
12+
line.remove_prefix(ansi_reset.size());
13+
}
14+
if (line.compare(0, SERVER_CHILD_STATE_PREFIX.size(), SERVER_CHILD_STATE_PREFIX) != 0) {
15+
return {};
16+
}
17+
return line;
18+
}

tools/server/server-models.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "server-common.h"
2+
#include "server-child-protocol.h"
23
#include "http.h"
34
#include "server-models.h"
45
#include "server-context.h"
@@ -42,7 +43,6 @@ extern char **environ;
4243
#define DEFAULT_STOP_TIMEOUT 10 // seconds
4344

4445
#define CMD_ROUTER_TO_CHILD_EXIT "cmd_router_to_child:exit"
45-
#define CMD_CHILD_TO_ROUTER_STATE "cmd_child_to_router:state:" // followed by json string
4646

4747
// address for child process, this is needed because router may run on 0.0.0.0
4848
// ref: https://github.com/ggml-org/llama.cpp/issues/17862
@@ -1053,9 +1053,10 @@ void server_models::load(const std::string & name, const load_options & opts) {
10531053
if (stdout_file) {
10541054
while (fgets(buffer, vec_buf.size(), stdout_file) != nullptr) {
10551055
std::string str(buffer);
1056-
if (string_starts_with(buffer, CMD_CHILD_TO_ROUTER_STATE)) {
1057-
LOG_DBG("[%5d] %s", port, buffer); // prevent spamming the log
1058-
this->handle_child_state(name, str);
1056+
std::string state_line(server_child_state_line(str));
1057+
if (!state_line.empty()) {
1058+
LOG_DBG("[%5d] %s", port, state_line.c_str()); // prevent spamming the log
1059+
this->handle_child_state(name, state_line);
10591060
} else {
10601061
// forward log
10611062
LOG("[%5d] %s", port, buffer);
@@ -1110,7 +1111,10 @@ void server_models::load(const std::string & name, const load_options & opts) {
11101111
}
11111112

11121113
child_proc->stopped.store(true, std::memory_order_release);
1113-
{
1114+
if (child_mode == SERVER_CHILD_MODE_DOWNLOAD) {
1115+
// Download children never enter stopping_models. Avoid the model lock after DOWNLOADED so remove() can join.
1116+
cv_stop.notify_all();
1117+
} else {
11141118
std::lock_guard<std::mutex> lk(this->mutex);
11151119
stopping_models.erase(name);
11161120
cv_stop.notify_all();
@@ -1525,7 +1529,7 @@ void server_models::handle_child_state(const std::string & name, const std::stri
15251529
json payload;
15261530

15271531
try {
1528-
json data = json::parse(raw_input.substr(strlen(CMD_CHILD_TO_ROUTER_STATE)));
1532+
json data = json::parse(raw_input.substr(SERVER_CHILD_STATE_PREFIX.size()));
15291533
state = server_state_from_str(json_value(data, "state", std::string()));
15301534
payload = json_value(data, "payload", json{});
15311535
} catch (const std::exception & e) {
@@ -1716,7 +1720,7 @@ void server_child::notify_to_router(const std::string & state, const json & payl
17161720
std::lock_guard<std::mutex> lk(mtx_stdout);
17171721
common_log_pause(common_log_main());
17181722
fflush(stdout);
1719-
fprintf(stdout, "%s%s\n", CMD_CHILD_TO_ROUTER_STATE, safe_json_to_str(data).c_str());
1723+
fprintf(stdout, "%s%s\n", SERVER_CHILD_STATE_PREFIX.data(), safe_json_to_str(data).c_str());
17201724
fflush(stdout);
17211725
common_log_resume(common_log_main());
17221726
}

tools/server/tests/unit/test_router.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,11 @@ def test_router_remote_preset():
504504
MODEL_DOWNLOAD_TIMEOUT = 30
505505

506506

507+
@pytest.fixture
508+
def private_router_download_cache(monkeypatch, tmp_path):
509+
monkeypatch.setenv("LLAMA_CACHE", str(tmp_path / "llama-cache"))
510+
511+
507512
def _listen_sse(
508513
server: ServerProcess, collected: list, stop: threading.Event, ready: threading.Event | None = None
509514
):
@@ -539,7 +544,7 @@ def _wait_for_sse_event(collected: list, event_type: str, model: str, timeout: i
539544
return False
540545

541546

542-
def test_router_download_model():
547+
def test_router_download_model(private_router_download_cache):
543548
"""Case 1: download a model, verify SSE events and GET /models."""
544549
global server
545550
server.start()
@@ -582,7 +587,7 @@ def test_router_download_model():
582587
assert MODEL_DOWNLOAD_ID in ids, f"{MODEL_DOWNLOAD_ID} not found in /models after download"
583588

584589

585-
def test_router_delete_model():
590+
def test_router_delete_model(private_router_download_cache):
586591
"""Case 2: delete the downloaded model, verify it disappears from GET /models."""
587592
global server
588593
server.start()

0 commit comments

Comments
 (0)