From b3d776adb46bd636882501b464bc4b3f6f6ccbb9 Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Mon, 22 Dec 2025 12:40:06 +0100 Subject: [PATCH] audit: kernel params bound and docgraph removal --- AUDITS.md | 26 ++- src/pel_stack/opreg/kernel_params.c | 3 + src/pel_stack/program_dag/program_dag.c | 62 ++---- tests/pel/test_pel_program_dag_exec.c | 65 +++++++ tests/pel/test_pel_surf_run.c | 20 +- tier1/opreg-tgk-docgraph-1.md | 240 ------------------------ 6 files changed, 104 insertions(+), 312 deletions(-) delete mode 100644 tier1/opreg-tgk-docgraph-1.md diff --git a/AUDITS.md b/AUDITS.md index 3ee5eb1..306c490 100644 --- a/AUDITS.md +++ b/AUDITS.md @@ -43,7 +43,7 @@ Status legend: ✅ completed, ⬜ pending. 15. ✅ `tier1/tgk-store-1.md` 16. ✅ `tier1/tgk-prov-1.md` 17. ✅ `tier1/opreg-pel1-kernel.md` -18. ⬜ `tier1/opreg-pel1-kernel-params-1.md` +18. ✅ `tier1/opreg-pel1-kernel-params-1.md` 19. ⬜ `tier1/opreg-tgk-docgraph-1.md` 20. ⬜ `tier1/amduat20-stack-overview.md` @@ -246,8 +246,22 @@ Status legend: ✅ completed, ⬜ pending. `AMDUAT_PEL_KERNEL_STATUS_INTERNAL`/`AMDUAT_PEL_KERNEL_STATUS_OOM`, which do not follow the `kernel_op_code << 16 | error_index` scheme and are not specified as kernel runtime error codes. -- Resolution: not addressed (needs spec clarification or implementation changes - to either remove/relocate `pel.bytes.params`, suppress diagnostics for kernel - runtime failures, and/or treat internal/OOM as out-of-model rather than - kernel runtime errors). -- Tests: not run. +- Resolution: documented `pel.bytes.params/1` in OPREG/PEL1-KERNEL and params + profile; missing global params now yields `INVALID_INPUTS`; Exec_DAG no longer + emits diagnostics for kernel op runtime failures; internal/OOM paths now + return out-of-model (no `ExecutionResultValue`), avoiding non-registry status + codes. +- Tests: `ctest --test-dir /home/niklas/build/amduat` (pass, 14 tests). + +## 2025-12-22 — OPREG/PEL1-KERNEL-PARAMS/1 (`tier1/opreg-pel1-kernel-params-1.md`) +- Scope: kernel params canonical encodings/decoding, size bounds, and + `INVALID_PROGRAM` mapping for param decode errors. +- Findings: `amduat_decode_const` accepts `params_bytes` longer than + `0xFFFF_FFFF` as long as `bytes.len` fits in `size_t`, but the spec requires + any kernel params payload length exceeding `u32::MAX` to be treated as a + decode error (even for non-`ENC/PEL-PROGRAM-DAG` inputs). +- Resolution: added a `params_bytes.len <= UINT32_MAX` guard in + `amduat_pel_kernel_params_decode` so all kernel param decodes enforce the + u32 bound; added a regression test that feeds an oversized `params_bytes` + length and expects `INVALID_PROGRAM`. +- Tests: user reported “100% tests passed, 0 tests failed out of 14”. diff --git a/src/pel_stack/opreg/kernel_params.c b/src/pel_stack/opreg/kernel_params.c index 775efcf..4864bbf 100644 --- a/src/pel_stack/opreg/kernel_params.c +++ b/src/pel_stack/opreg/kernel_params.c @@ -115,6 +115,9 @@ bool amduat_pel_kernel_params_decode( if (desc == NULL || out_params == NULL) { return false; } + if (params_bytes.len > UINT32_MAX) { + return false; + } out_params->kind = desc->kind; switch (desc->kind) { diff --git a/src/pel_stack/program_dag/program_dag.c b/src/pel_stack/program_dag/program_dag.c index f51c786..962fc1e 100644 --- a/src/pel_stack/program_dag/program_dag.c +++ b/src/pel_stack/program_dag/program_dag.c @@ -823,13 +823,8 @@ static bool amduat_pel_program_dag_exec_internal( amduat_prepared_reset(&prepared); prep_result = amduat_program_prepare(program, &prepared, &prep_error); if (prep_result == AMDUAT_PEL_PROGRAM_PREP_OOM) { - amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED, - AMDUAT_PEL_EXEC_ERROR_RUNTIME, - AMDUAT_PEL_KERNEL_STATUS_OOM); - amduat_diag_setf(out_result, - AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED, - "runtime failed: out of memory"); - return true; + amduat_prepared_free(&prepared); + return false; } if (prep_result != AMDUAT_PEL_PROGRAM_PREP_OK) { amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_INVALID_PROGRAM, @@ -988,13 +983,7 @@ static bool amduat_pel_program_dag_exec_internal( program->nodes_len, sizeof(*node_results)); if (node_results == NULL) { amduat_prepared_free(&prepared); - amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED, - AMDUAT_PEL_EXEC_ERROR_RUNTIME, - AMDUAT_PEL_KERNEL_STATUS_OOM); - amduat_diag_setf(out_result, - AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED, - "runtime failed: out of memory"); - return true; + return false; } for (i = 0; i < program->nodes_len; ++i) { node_results[i].status = AMDUAT_PEL_NODE_TRACE_SKIPPED; @@ -1015,13 +1004,7 @@ static bool amduat_pel_program_dag_exec_internal( if (resolved_inputs == NULL) { amduat_node_results_free(node_results, program->nodes_len); amduat_prepared_free(&prepared); - amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED, - AMDUAT_PEL_EXEC_ERROR_RUNTIME, - AMDUAT_PEL_KERNEL_STATUS_OOM); - amduat_diag_setf(out_result, - AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED, - "runtime failed: out of memory"); - return true; + return false; } } @@ -1091,33 +1074,18 @@ static bool amduat_pel_program_dag_exec_internal( &prepared.params[node_index], params, &node_results[node_index].outputs, &node_results[node_index].outputs_len, &status_code)) { - if (status_code == AMDUAT_PEL_KERNEL_STATUS_OOM) { + if (status_code == AMDUAT_PEL_KERNEL_STATUS_OOM || + status_code == AMDUAT_PEL_KERNEL_STATUS_INTERNAL || + status_code == 0) { free(resolved_inputs); resolved_inputs = NULL; - goto oom_finish; - } - if (status_code == 2 || status_code == 3 || status_code == 0) { - status_code = 1; + goto env_fail; } node_results[node_index].status = AMDUAT_PEL_NODE_TRACE_FAILED; node_results[node_index].status_code = status_code; - (void)amduat_node_diag_setf( - &node_results[node_index], - AMDUAT_PEL_DAG_DIAG_NODE_RUNTIME_FAILED, - "runtime failed: node %u (%.*s@%u) status_code %u", - (unsigned int)node->id, - (int)node->op.name.len, - (const char *)node->op.name.data, - (unsigned int)node->op.version, - (unsigned int)status_code); any_node_executed = true; amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED, AMDUAT_PEL_EXEC_ERROR_RUNTIME, status_code); - amduat_diag_setf(out_result, - AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED, - "runtime failed: node %u status_code %u", - (unsigned int)node->id, - (unsigned int)status_code); free(resolved_inputs); goto finish; } @@ -1132,7 +1100,7 @@ static bool amduat_pel_program_dag_exec_internal( if (*out_outputs == NULL) { free(resolved_inputs); resolved_inputs = NULL; - goto oom_finish; + goto env_fail; } } @@ -1166,7 +1134,7 @@ static bool amduat_pel_program_dag_exec_internal( *out_outputs_len = 0; free(resolved_inputs); resolved_inputs = NULL; - goto oom_finish; + goto env_fail; } } @@ -1195,13 +1163,7 @@ finish: amduat_prepared_free(&prepared); return true; -oom_finish: - amduat_set_result(out_result, AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED, - AMDUAT_PEL_EXEC_ERROR_RUNTIME, - AMDUAT_PEL_KERNEL_STATUS_OOM); - amduat_diag_setf(out_result, - AMDUAT_PEL_DAG_DIAG_RUNTIME_FAILED, - "runtime failed: out of memory"); +env_fail: if (out_outputs != NULL && *out_outputs != NULL) { amduat_pel_program_dag_free_outputs(*out_outputs, *out_outputs_len); *out_outputs = NULL; @@ -1223,7 +1185,7 @@ oom_finish: node_results = NULL; } amduat_prepared_free(&prepared); - return true; + return false; } bool amduat_pel_program_dag_exec( diff --git a/tests/pel/test_pel_program_dag_exec.c b/tests/pel/test_pel_program_dag_exec.c index 27a7484..a8fed0d 100644 --- a/tests/pel/test_pel_program_dag_exec.c +++ b/tests/pel/test_pel_program_dag_exec.c @@ -2,6 +2,7 @@ #include "amduat/hash/asl1.h" #include "amduat/pel/run.h" #include +#include #include #include #include @@ -247,6 +248,67 @@ static int test_invalid_params(void) { return 0; } +static int test_oversized_params_len(void) { + amduat_pel_dag_input_t node_inputs[1]; + amduat_pel_node_t nodes[1]; + amduat_pel_root_ref_t roots[1]; + amduat_pel_program_t program; + amduat_artifact_t inputs[1]; + amduat_artifact_t *outputs = NULL; + size_t outputs_len = 0; + amduat_pel_execution_result_value_t result; + const char op_concat[] = "pel.bytes.concat"; + uint8_t params_byte = 0x00u; + size_t oversized_len; + + if (SIZE_MAX <= UINT32_MAX) { + return 0; + } + + oversized_len = (size_t)UINT32_MAX + 1u; + memset(&result, 0, sizeof(result)); + + node_inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + node_inputs[0].value.external.input_index = 0; + + nodes[0].id = 1; + nodes[0].op.name = amduat_octets(op_concat, strlen(op_concat)); + nodes[0].op.version = 1; + nodes[0].inputs = node_inputs; + nodes[0].inputs_len = 1; + nodes[0].params = amduat_octets(¶ms_byte, oversized_len); + + roots[0].node_id = 1; + roots[0].output_index = 0; + + program.nodes = nodes; + program.nodes_len = 1; + program.roots = roots; + program.roots_len = 1; + + inputs[0] = amduat_artifact(amduat_octets("x", 1)); + + if (!amduat_pel_program_dag_exec(&program, inputs, 1, NULL, &outputs, + &outputs_len, &result)) { + fprintf(stderr, "exec failed\n"); + amduat_pel_execution_result_free(&result); + return 1; + } + + if (result.status != AMDUAT_PEL_EXEC_STATUS_INVALID_PROGRAM || + result.summary.kind != AMDUAT_PEL_EXEC_ERROR_PROGRAM || + result.summary.status_code != 2) { + fprintf(stderr, "unexpected oversized params status\n"); + amduat_pel_program_dag_free_outputs(outputs, outputs_len); + amduat_pel_execution_result_free(&result); + return 1; + } + + amduat_pel_program_dag_free_outputs(outputs, outputs_len); + amduat_pel_execution_result_free(&result); + return 0; +} + static int test_invalid_input_index(void) { amduat_pel_dag_input_t node_inputs[1]; amduat_pel_node_t nodes[1]; @@ -363,6 +425,9 @@ int main(void) { if (test_invalid_params() != 0) { return 1; } + if (test_oversized_params_len() != 0) { + return 1; + } if (test_invalid_input_index() != 0) { return 1; } diff --git a/tests/pel/test_pel_surf_run.c b/tests/pel/test_pel_surf_run.c index 92d1027..d2af833 100644 --- a/tests/pel/test_pel_surf_run.c +++ b/tests/pel/test_pel_surf_run.c @@ -770,14 +770,11 @@ static int test_surf_runtime_failure_trace_diag(void) { amduat_pel_surface_execution_result_t decoded; amduat_artifact_t trace_artifact; amduat_pel_trace_dag_value_t trace; - char expected_msg[128]; - int expected_len; int exit_code = 1; uint8_t payload_a[] = {'a'}; uint8_t payload_b[] = {'b'}; amduat_artifact_t input_a; amduat_artifact_t input_b; - const uint32_t diag_code = 0x00030002u; cfg.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1; cfg.hash_id = AMDUAT_HASH_ASL1_ID_SHA256; @@ -861,20 +858,11 @@ static int test_surf_runtime_failure_trace_diag(void) { } artifact_free(&trace_artifact); - expected_len = snprintf(expected_msg, sizeof(expected_msg), - "runtime failed: node 1 (pel.bytes.concat@1) " - "status_code %u", - (unsigned int) - AMDUAT_PEL_KERNEL_STATUS_CONCAT_TYPE_TAG_MISMATCH); - if (expected_len < 0 || - trace.node_traces_len != 1 || + if (trace.node_traces_len != 1 || trace.node_traces[0].status != AMDUAT_PEL_NODE_TRACE_FAILED || - trace.node_traces[0].diagnostics_len < 1 || - trace.node_traces[0].diagnostics[0].code != diag_code || - trace.node_traces[0].diagnostics[0].message.len != - (size_t)expected_len || - memcmp(trace.node_traces[0].diagnostics[0].message.data, - expected_msg, (size_t)expected_len) != 0) { + trace.node_traces[0].status_code != + AMDUAT_PEL_KERNEL_STATUS_CONCAT_TYPE_TAG_MISMATCH || + trace.node_traces[0].diagnostics_len != 0) { fprintf(stderr, "trace runtime diagnostics mismatch\n"); amduat_enc_pel_trace_dag_free(&trace); goto cleanup_decoded; diff --git a/tier1/opreg-tgk-docgraph-1.md b/tier1/opreg-tgk-docgraph-1.md deleted file mode 100644 index 7073109..0000000 --- a/tier1/opreg-tgk-docgraph-1.md +++ /dev/null @@ -1,240 +0,0 @@ -# OPREG/TGK-DOCGRAPH/1 — Document Graph Registry - -Status: Draft -Owner: Architecture -Version: 0.1.0 -SoT: Plan -Last Updated: 2025-12-01 -Linked Phase Pack: PH12 -Tags: [registry, tgk, docgraph] - - - -**Document ID:** `OPREG/TGK-DOCGRAPH/1` -**Layer:** L1 Profile (TGK Doc Graph Registry over `TGK/1-CORE` + `ENC/TGK1-EDGE/1`) - -**Depends on (normative):** - -* `ASL/1-CORE v0.4.x` — `Artifact`, `Reference`, `TypeTag`, `HashId` -* `ENC/ASL1-CORE v1.x` — canonical encodings for Artifacts and References -* `HASH/ASL1 v0.2.x` — ASL1 hash family (`HASH-ASL1-256`) -* `TGK/1-CORE v0.7.x` — trace graph kernel: `Node`, `EdgeBody`, `EdgeTypeId` -* `ENC/TGK1-EDGE/1 v0.1.x` — canonical encoding for `EdgeBody` / EdgeArtifacts -* `AMDUAT-DOCID` (Tier-0) — document identity and SoT/surface model - -**Integrates with (informative):** - -* `TGK/STORE/1` — graph store/query profile over ASL/1-STORE + TGK -* ADR-032 and PH10/PH12 import designs (RΩ / export) -* Future doc graph consumers (assistant overlays, IDX, provenance views) - -© 2025 Amduat Programme. - -## License - -Except where otherwise noted, this document (text and diagrams) is licensed under -the Creative Commons Attribution 4.0 International License (CC BY 4.0). - -The identifier registries and mapping tables (e.g. TypeTag IDs, HashId -assignments, EdgeTypeId tables) are additionally made available under CC0 1.0 -Universal (CC0) to enable unrestricted reuse in implementations and derivative -specifications. - -Code examples in this document are provided under the Apache License 2.0 unless -explicitly stated otherwise. Test vectors, where present, are dedicated to the -public domain under CC0 1.0. - ---- - -## 0. Purpose and Non-Goals - -### 0.1 Purpose - -`OPREG/TGK-DOCGRAPH/1` defines a **doc/import/navigation graph registry** for Amduat: - -* It names **node concepts** (as ASL/1 Artifacts) for: - * conceptual documents (DOCID lineages), - * document versions at a given snapshot (e.g. RΩ), - * Git commits and blobs, - * Amduat SoT instances. -* It names **edge types** (`EdgeTypeId`s) that connect those concepts: - * document ↔ version, surface, SoT state, - * version ↔ Git blob/commit, - * document ↔ Amduat instance. -* It constrains how those edges are represented as EdgeArtifacts under - `ENC/TGK1-EDGE/1` and consumed via `TGK/STORE/1`. - -This registry is intentionally **doc/import scoped**. Execution, fact, and -certificate edges live in their own TGK/OPREG registries and MUST NOT reuse -`EdgeTypeId` assignments from this doc graph registry. - -This Tier-1 stub is the **canonical registry companion** to the PH12 design -note `PH12-EV-IMPORT-001 — Doc Graph OPREG Profile Design -(/logs/ph12/evidence/import/PH12-EV-IMPORT-001/opreg-tgk-docgraph-design-20251201.md)`, -which records design intent and sandbox experience; this document is the SoT -for the node and edge vocabulary. - -### 0.2 Non-goals - -This registry does **not** define: - -* any storage API (`ASL/1-STORE`, `TGK/STORE/1` already cover that), -* any provenance algorithms or queries (`TGK/PROV/1` and higher layers), -* any assistant or overlay behavior (those consume this registry), -* concrete import/export profiles (ADR-032 handles those). - -It only defines **concepts and edge types**; encoding and storage use existing -Tier-1 profiles. - ---- - -## 1. Node Concepts (Informative overview) - -This section summarizes node concepts; canonical encodings and type_tags are -defined in companion encoding profiles (TBD). - -### 1.1 DOC_CONCEPT - -Conceptual governed document identity per `AMDUAT-DOCID`: - -* `identity_authority` (string), -* `lineage_id` (string), -* optional `doc_code` (string), -* optional `code_status` (e.g. `tentative`, `stable`). - -There is exactly one `DOC_CONCEPT` node per `(identity_authority, lineage_id)`. - -### 1.2 DOC_VERSION - -Versioned SoT slice of a governed document at a snapshot commit: - -* `identity_authority`, `lineage_id`, `doc_code`, `code_status`, -* `g_commit` (Git commit id), -* `sha256` (content hash of the doc bytes at `g_commit`), -* `path` (repository path at `g_commit`, e.g. `/amduat/tier0/docid.md`), -* `surface`, `sot` (SoT state) per DOCID header. - -Multiple `DOC_VERSION` nodes may exist for a `DOC_CONCEPT` across commits. - -### 1.3 GIT_COMMIT - -Git commit metadata: - -* `commit` (sha1), -* `parents` (list of parent commit ids), -* `tree` (tree id), -* `author_name`, `author_email`, `authored_at`, -* `committer_name`, `committer_email`, `committed_at`, -* summary or truncated message. - -### 1.4 GIT_BLOB - -Content snapshot for a single blob at `g_commit`: - -* `blob_sha` (sha1), -* `sha256` (content hash), -* `size_bytes`, -* `mode` (tree mode, including exec/symlink bits), -* `path` at `g_commit`. - -### 1.5 AMDUAT_INSTANCE - -Descriptor for an Amduat SoT instance: - -* `g_commit` (RΩ commit), -* `store_root` (SoT store root), -* `store_backend_id`, -* references to RΩ FER/1 receipts and manifests, -* optional labels (environment, hostname, etc.). - -### 1.6 Helper nodes - -* `SURFACE` — surface classification nodes (e.g. `tier0`, `tier1`, `phase`, `evidence`). -* `SOT_STATE` — SoT state nodes (`Yes`, `Plan`, `Ref`). - ---- - -## 2. Edge Types (Doc Graph Domain) - -`EdgeTypeId` values in this registry are reserved for doc/import/navigation -edges. Concrete numeric assignments live in the encoding/catalogue layer. - -Implementations and other OPREG registries MUST treat these `EdgeTypeId`s as -belonging exclusively to the **Amduat doc graph domain**: - -* the eventual allocation for this registry is expected to reserve a contiguous - `EdgeTypeId` band (informally: an `AMDUAT-DOCGRAPH` band), -* only doc/import/navigation semantics (edges in §§2.1–2.4) may occupy that - band, -* PEL execution, FER/1, CIL, FCT, and other TGK domains MUST use their own - registries and bands. - -### 2.1 Identity & version edges - -* `EDGE_DOC_HAS_VERSION` - `DOC_CONCEPT → DOC_VERSION` — this version belongs to this conceptual document. - -* `EDGE_VERSION_OF` - `DOC_VERSION → DOC_CONCEPT` — reverse link; derivable from `EDGE_DOC_HAS_VERSION`. - -* `EDGE_DOC_HAS_IDENTITY` - `DOC_VERSION → DOC_CONCEPT` — DOCID identity is attached to this version. - -### 2.2 Surface & SoT edges - -* `EDGE_DOC_ON_SURFACE` - `DOC_VERSION → SURFACE` — surface classification (governance/spec/phase/evidence). - -* `EDGE_DOC_SOT` - `DOC_VERSION → SOT_STATE` — SoT status (`Yes`, `Plan`, `Ref`) for this version. - -### 2.3 Git provenance edges - -* `EDGE_VERSION_HAS_BLOB` - `DOC_VERSION → GIT_BLOB` — ties a document version to the blob at `g_commit`. - -* `EDGE_VERSION_FROM_COMMIT` - `DOC_VERSION → GIT_COMMIT` — last commit that touched this path at/before the snapshot. - -### 2.4 SoT instance edges - -* `EDGE_DOC_MEMBER_OF_AMDUAT` - `DOC_CONCEPT → AMDUAT_INSTANCE` — this document is part of a particular Amduat instance. - ---- - -## 3. Encoding & Store Integration (Summary) - -All doc-graph edges: - -* are represented as TGK `EdgeBody` values with `EdgeTypeId` from this registry, -* are encoded as EdgeArtifacts via `ENC/TGK1-EDGE/1` using `TYPE_TAG_TGK1_EDGE_V1`, -* derive `EdgeRef` identities via `HASH/ASL1` over `EdgeBytes`, -* live in ASL/1-STORE instances alongside other Artifacts. - -Nodes (`DOC_CONCEPT`, `DOC_VERSION`, `GIT_COMMIT`, `GIT_BLOB`, `AMDUAT_INSTANCE`, etc.) are ordinary -ASL/1 Artifacts; their `Reference`s are the TGK nodes. - -`TGK/STORE/1` provides query semantics over the resulting graph. - -JSON overlays or other projected views (for example, PH12 doc graph sandboxes) -MAY be emitted for human navigation and experiments, but they are always -derived from the underlying node Artifacts and EdgeArtifacts governed by this -registry and `ENC/TGK1-EDGE/1`; overlays are never the source of truth for -doc graph semantics. - ---- - -## 4. Ingest & Encoder Interaction (Informative) - -Implementations are expected to: - -* materialise node Artifacts per this registry (and companion encoding profiles), -* emit FER/1 receipts for ingest pipelines, -* emit an idempotent edge worklist (doc-edge queue) that references `EdgeTypeId`s - from this registry and node `Reference`s, -* use a separate encoder to turn worklist items into EdgeArtifacts using `ENC/TGK1-EDGE/1`, - writing them into ASL/1-STORE for consumption via `TGK/STORE/1`. - -Details of worklist format and encoder scheduling are left to PH12/PHB01 -implementation notes; this registry only fixes the conceptual node/edge space.