diff --git a/research/h25-private-binary-dispatch/EXACT-NATIVE-CHANGE.diff b/research/h25-private-binary-dispatch/EXACT-NATIVE-CHANGE.diff new file mode 100644 index 0000000000..a9d2c8b9d9 --- /dev/null +++ b/research/h25-private-binary-dispatch/EXACT-NATIVE-CHANGE.diff @@ -0,0 +1,152 @@ +--- predecessor/implementation/native/science_view_entry.c ++++ candidate/implementation/native/science_view_entry.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -40,6 +41,9 @@ + #error The entry is the reviewed native LP64 x86-64 ABI only + #endif + _Static_assert(sizeof(void *) == 8 && sizeof(long) == 8, "LP64 required"); ++#if !defined(SYS_fsopen) || !defined(SYS_fsconfig) ++#error Selected Linux x86-64 build headers must declare fsopen and fsconfig syscall numbers ++#endif + + /* Selected v6.12 UAPI shapes, intentionally independent of libc header age. */ + struct sv_mount_attr { uint64_t attr_set, attr_clr, propagation, userns_fd; }; +@@ -52,6 +56,10 @@ + #define SV_MOVE_TO_EMPTY 0x40U + #define SV_MOUNT_RDONLY UINT64_C(1) + #define SV_MOUNT_NOSUID UINT64_C(2) ++/* Linux-v6.12 include/uapi/linux/mount.h values. Local names avoid requiring ++ * mutually varying libc/kernel mount-header enum definitions in one TU. */ ++#define SV_FSOPEN_CLOEXEC 1U ++#define SV_FSCONFIG_CMD_CREATE 6U + #define SV_FS_EXEC (UINT64_C(1)<<0) + #define SV_FS_WRITE (UINT64_C(1)<<1) + #define SV_FS_READ (UINT64_C(1)<<2) +@@ -87,6 +95,7 @@ + static bool uncertain, error_overflow, user_entered, mount_entered; + static bool root_private, root_readonly, artifact_attached, usage_attached; + static bool landlocked, filtered; ++static bool dispatch_attempted, dispatch_created, dispatch_context_closed; + static uid_t uid; + static gid_t gid; + static int stdin_status_flags; +@@ -358,6 +367,34 @@ + mount_entered=true; + if(mount(NULL,"/",NULL,MS_REC|MS_PRIVATE,NULL)<0)return failed("recursive-private-root"); + root_private=true;return true; ++} ++static bool create_private_binfmt_dispatch(void) { ++ /* The selected operating TCB is Linux-v6.12 CONFIG_BINFMT_MISC=y with ++ * successful built-in filesystem registration. This is a precise source ++ * selection, never a supplied flag or host-support claim: get_fs_type must ++ * find the registered implementation without request_module. Actual image ++ * association is a separate mandatory parent admission before execution. ++ * ++ * In our fresh user namespace bm_fill_super initializes an empty list and ++ * publishes user_ns->binfmt_misc. No fsmount/visible mount is constructed. ++ * Closing this unattached context drops its superblock, not that namespace ++ * instance. It continues to shadow every ancestor handler until userns ++ * destruction; later creation/registration/import is denied by lockdown. ++ */ ++ if(!require(user_entered&&mount_entered&&root_private&&!dispatch_attempted&& ++ !root_readonly&&!landlocked&&!filtered,"once-private-dispatch-before-view-and-lockdown"))return false; ++ int slot=reserve("private-binfmt-fsopen");if(slot<0)return false; ++ dispatch_attempted=true; ++ int context=acquired(slot,syscall(SYS_fsopen,"binfmt_misc",SV_FSOPEN_CLOEXEC)); ++ if(context<0)return false; ++ bool created=true; ++ if(syscall(SYS_fsconfig,context,SV_FSCONFIG_CMD_CREATE,NULL,NULL,0)<0) { ++ /* Save the actual create errno BEFORE the once-close can replace it. */ ++ created=failed("private-binfmt-fsconfig-create"); ++ } else dispatch_created=true; ++ bool closed=close_once(context); ++ if(closed)dispatch_context_closed=true; ++ return created&&closed&&!uncertain; + } + static int clone_mount(int fd,const char *site) { + int slot=reserve(site);if(slot<0)return -1; +@@ -614,9 +651,10 @@ + char out[SV_REPORT_CAP];size_t used=0; + #define APPEND(...) do { int amount=snprintf(out+used,sizeof out-used,__VA_ARGS__); \ + if(amount<0||(size_t)amount>=sizeof out-used){uncertain=true;goto emit;} used+=(size_t)amount; } while(0) +- APPEND("{\"schema\":\"h25-native-view-setup-failure-v1\",\"exit\":78,\"science_started\":false,\"uncertain\":%s,\"error_overflow\":%s,\"user_entered\":%s,\"mount_entered\":%s,\"root_private\":%s,\"root_readonly\":%s,\"artifact_attached\":%s,\"usage_attached\":%s,\"landlocked\":%s,\"filtered\":%s,\"errors\":[", ++ APPEND("{\"schema\":\"h25-native-view-setup-failure-v1\",\"exit\":78,\"science_started\":false,\"uncertain\":%s,\"error_overflow\":%s,\"user_entered\":%s,\"mount_entered\":%s,\"root_private\":%s,\"root_readonly\":%s,\"artifact_attached\":%s,\"usage_attached\":%s,\"landlocked\":%s,\"filtered\":%s,\"private_dispatch_attempted\":%s,\"private_dispatch_created\":%s,\"private_dispatch_context_closed\":%s,\"errors\":[", + uncertain?"true":"false",error_overflow?"true":"false",user_entered?"true":"false",mount_entered?"true":"false", +- root_private?"true":"false",root_readonly?"true":"false",artifact_attached?"true":"false",usage_attached?"true":"false",landlocked?"true":"false",filtered?"true":"false"); ++ root_private?"true":"false",root_readonly?"true":"false",artifact_attached?"true":"false",usage_attached?"true":"false",landlocked?"true":"false",filtered?"true":"false", ++ dispatch_attempted?"true":"false",dispatch_created?"true":"false",dispatch_context_closed?"true":"false"); + for(size_t i=0;iacquired->once-close; raw syscall errors ordered; no external library finalizer | Source-defined close states plus actual process exit; failure projection is non-success | + | User/mount namespace and mapping writes | Entry creates no descendant; records each completed irreversible stage and every failed syscall | Kernel releases remaining namespace references on actual waited process exit; no self-issued release receipt | + | Detached clones | Entry owns actual open_tree FDs; partial attach remains recorded; all known FD closes attempted once | Actual process exit releases the private mount namespace; no unmount/rollback retry | ++| Private binary-format context/instance | Entry reserves/acquires one real fs-context FD, creates once, saves create error before close error and retires/closes once before view; no context retry or mount | Context close releases its superblock; the empty per-user-namespace instance remains until namespace destruction. Failed create may have published before a later LSM failure, so actual process/userns retirement is required and no original optimised-refusal classification is allowed | + | Artifact and usage backing objects | Original runner/registry owns retained paths; entry exposes only exact pinned objects | Original retained-tree registry/oracle/archive custody; entry never unlinks or repairs them | + | Landlock ruleset/path FDs | Entry source-owned; mandatory restriction failure prevents exec; close error is uncertain | Once-close states and external exit; restrictions inherited only after successful restrict_self | + | Final filter/exec | Entry installs mandatory filter and execs exact original observer prefix | Actual outer child launch/wait/streams and complete receiver, never a pre-exit native success marker | +@@ -81,7 +82,13 @@ + + The following are outside this source's self-authentication and must be genuine original parent/profile facts: (a) the fresh trusted runner and this actual loaded native image/loader with a clean initial environment and no prior descendant; (b) selected Linux-v6.12 semantics, or source-reviewed equivalent, and actual namespace/mount/Landlock/seccomp support; (c) a bounded stable mount topology with no unreviewed automount, remote/delegated filesystem or kernel service effect bypass; (d) exact original EX ownership/no external mutation across object validation, mount setup and final pathname exec; (e) original full native/interpreter/compiler/library/source association without trusting compiler behavior; (f) actual G domain placement before birth, owner survival and retirement; (g) selected self resource/fault-control compatibility, including CORE(1,1); (h) enclosing budget/cost coverage for every reached setup, failure and terminal tail. An argv pin, JSON hash or native failure flag establishes none of these facts on its own. + +-One concrete loader premise within (b,c,e) is **no reachable preopened/delegated binfmt interpreter that reintroduces an old mount reference**. Linux-v6.12 binfmt_misc can inherit ancestor handlers, and an F registration uses `file_clone_open` on its previously opened interpreter. A child could select the registered format under broad execute permission; merely closing user FDs and denying mount syscalls would not remove that kernel-held file. An executable exposed through the resulting `/proc/self/exe` may therefore require an additional old-reference argument. A sufficient source profile has no enabled reachable F/delegated handler and uses the selected ordinary ELF/script loaders, with their opens through the final view. If registrations are present, their exact ownership, accessible old references and consequences need source analysis; their harmlessness is not inferred from compiler trust. This entry has not probed registrations, disables no host handler and makes no current-host absence claim. The existing native/image association must establish this fact or leave the view unadmitted. ++The former ancestor-binfmt absence premise is replaced by an **implemented entry owner**. Linux-v6.12 `load_binfmt_misc` stops at the first non-NULL per-user-namespace instance; `bm_fill_super` allocates an empty entries list and publishes it in the freshly created user namespace. Actual fsopen/fsconfig CREATE reaches this callback without attaching a visible mount. Its context close invokes `put_fs_context` and deactivates the superblock; `bm_put_super` drops its userns reference but does not erase the namespace's instance. Consequently the empty instance continues shadowing every ancestor F/preopened interpreter until userns destruction. No descendant exists during setup; all descendants inherit the final namespace and policy. Later fsopen/fsconfig/fsmount/fspick, namespace/mount changes and descriptor import are denied. Existing ancestor registry paths stay read-only with no Landlock write rule; the new empty registry has no visible mount at all. ++ ++The selected native interface profile explicitly requires **Linux v6.12 CONFIG_BINFMT_MISC=y and successful built-in filesystem registration**. `get_fs_type` must find that exact already-registered implementation before reaching its request_module branch. This is an operating source/configuration selection whose genuine installed-image association belongs to the parent before execution; neither this document nor profile DATA proves it current. No available/compatible boolean, host probe, module request, host-handler disable or trusted compiler is substituted. Build headers must supply native x86-64 SYS_fsopen/SYS_fsconfig; local prefixed UAPI constants retain v6.12 values FSOPEN_CLOEXEC=1 and FSCONFIG_CMD_CREATE=6 without mixing libc/kernel enum definitions. `` explicitly declares GNU `unshare`. ++ ++Publication is not assumed atomic with the whole fsconfig syscall: vfs_get_tree/bm_fill_super can publish before a later security_sb_kern_mount failure. The entry records `private_dispatch_created=true` only after a successful return; false is **not an assertion that no partial instance exists**. Both a failed create and a failed/uncertain close remain paid setup failures before science, with actual raw errno order and namespace/process teardown obligations. They cannot count as the original optimised refusal. A successful private instance closes the old-reference confinement edge only. The separate finite original-exec/fault/LSM/topology proof must show that shadowing handlers preserves every required GNU-time, Python, compiler/helper, script and failed-exec observation; if an original required case relies on a misc handler, whole acceptance remains blocked. Actual current kernel/image support, numerical fit and execution admission remain later. ++ ++The exact successful-path FD bound is27 lifetime registry births, increased from26 by this one context; cap96 is unchanged, leaving69 slots. The ordered birth groups are startup census/stdin/task/children4; entry/self/time3; ID-map writes3; private context1; artifact/usage/cwd3; detached clones2; copied root/final cwd2; final devnull/dup0 two; Landlock ruleset/root/artifact/usage/devnull5; final census1; final time image1. The context closes before any view handles, so peak stays13 tracked live FDs, or15 actual including the unregistered stdout/stderr writer endpoints. On failure no later acquisition is attempted. At the maximum view/Landlock point at most12 setup FDs require close plus one primary error, so the existing48-error capacity covers that13-error bound; context create+close has at most two errors in original order. FD0/pipe release and all namespace publication/retirement costs remain in the enclosing attempt and are not declared free. + + The joined Q_sigma proof must separately map copied source attributes, default ACL/creation ancestors, executable/library attributes, original negative fixtures and within-invocation custody metadata to actual consumers for every required case. It must preserve the original outer C2 retention listxattr refusal on every retained root/file/directory. Same backing inode or source bytes do not prove that an ACL copied by CPython in the one-ID namespace is unchanged; an omitted consequential ACL can erase an original retention refusal. Nonempty attributes require consequence analysis. Entry contains no synthetic refusal or absence-of-xattrs assertion. Current attributes, current kernel/native images, original negative-case preservation, complete source acceptance and execution GO remain unissued by this native component. + +@@ -97,4 +104,5 @@ + - [Linux v6.12 clone implementation](https://github.com/torvalds/linux/blob/v6.12/kernel/fork.c): SYSCALL_DEFINE0 fork/vfork and lower_32_bits in legacy clone. + - [Linux v6.12 coredump](https://github.com/torvalds/linux/blob/v6.12/fs/coredump.c): limit==1 aborts pipe dispatch before usermodehelper; file dispatch enforces min_coredump. + - [Linux v6.12 binfmt_misc](https://github.com/torvalds/linux/blob/v6.12/fs/binfmt_misc.c): load_binfmt_misc ancestor lookup, MISC_FMT_OPEN_FILE/file_clone_open and F registration; [exec implementation](https://github.com/torvalds/linux/blob/v6.12/fs/exec.c) retains the actual interpreter execution/custody obligation. ++- [Linux v6.12 fsopen/fsconfig](https://github.com/torvalds/linux/blob/v6.12/fs/fsopen.c), [context release](https://github.com/torvalds/linux/blob/v6.12/fs/fs_context.c), [filesystem registration lookup](https://github.com/torvalds/linux/blob/v6.12/fs/filesystems.c), [mount UAPI](https://github.com/torvalds/linux/blob/v6.12/include/uapi/linux/mount.h), and [user namespace destruction](https://github.com/torvalds/linux/blob/v6.12/kernel/user_namespace.c) support the actual private-dispatch construction, exact builtin selection and remaining lifetime edge. + - [Linux v6.12 open metadata](https://github.com/torvalds/linux/blob/v6.12/fs/open.c), [utimes](https://github.com/torvalds/linux/blob/v6.12/fs/utimes.c), [xattr](https://github.com/torvalds/linux/blob/v6.12/fs/xattr.c) and [POSIX ACL](https://github.com/torvalds/linux/blob/v6.12/fs/posix_acl.c): mount-write and namespace representation dependencies. +--- predecessor/implementation/bindings/profiles/native.json ++++ candidate/implementation/bindings/profiles/native.json +@@ -9,7 +9,12 @@ + "G survives covered role deaths then wholeD retirement; killrequest/frozen/membership/populationempty/waits/FDclose are distinct conjunctions", + "G loss/unknown leaves persistent incomplete and no fresh selected operation/retention success", + "SourceSHA corroborates original external actualentry association; installed nativeimage/current runtime identity not proved by sourcehash", +- "Science entry source, actual built image/source association, original time image, namespace/LL ABI6/seccomp semantics and finite observation Q require independently qualified current input; authoring grants no execution" ++ "Science entry source, actual built image/source association, original time image, namespace/LL ABI6/seccomp semantics and finite observation Q require independently qualified current input; authoring grants no execution", ++ "Selected native dispatch source profile: Linux v6.12 CONFIG_BINFMT_MISC=y, successful built-in init_misc_binfmt/register_filesystem registration of the exact binfmt_misc implementation before this entry; get_fs_type resolves the already registered built-in without request_module, not a supplied available or compatible flag and not an observed host fact", ++ "After fresh same-ID user/private mount namespaces, actual entry reserves/acquires one fsopen(binfmt_misc,FSOPEN_CLOEXEC) context, calls fsconfig(FSCONFIG_CMD_CREATE) once, records raw create errno before one close, and closes before view/capability drop/descendants; no fsmount or visible registry mount", ++ "Linux v6.12 bm_fill_super publishes the fresh user namespace empty binfmt_misc instance; the unattached context may close while that instance shadows ancestor handlers through namespace lifetime; any failed create may have partial publication, remains paid setup failure before science and requires actual namespace/process retirement", ++ "Final inherited seccomp denies fsopen/fsconfig/fsmount/fspick/mount APIs and descriptor import; no new registry mount is exposed and copied ancestor registry paths have neither mount-write nor Landlock write authority; required original exec/CORE/LSM/topology preservation needs the separate finite consequence proof", ++ "Native source headers: GNU sched.h supplies unshare declaration; LP64 x86-64 SYS_fsopen/SYS_fsconfig syscall numbers required at build, v6.12 local UAPI FSOPEN_CLOEXEC=1 and FSCONFIG_CMD_CREATE=6; no compiler or native execution implied" + ], + "runtime_admission": "UNISSUED", + "schema": "s03-source-operating-profile-v1", diff --git a/research/h25-private-binary-dispatch/INVENTORY.json b/research/h25-private-binary-dispatch/INVENTORY.json new file mode 100644 index 0000000000..996fee4276 --- /dev/null +++ b/research/h25-private-binary-dispatch/INVENTORY.json @@ -0,0 +1,114 @@ +{ + "schema": "private-binary-dispatch-package-inventory-v1", + "files": { + "EXACT-NATIVE-CHANGE.diff": { + "bytes": 22597, + "sha256": "c8fb382905796457e085c9416a4bb462532e353c8adc0d5fdea9c2b93befea75" + }, + "PROVENANCE.json": { + "bytes": 4089, + "sha256": "a6139d71a6390dc5bba7cf078edac86a126b373ef10bd26c148a3038f35fb8af" + }, + "README.md": { + "bytes": 7761, + "sha256": "ae2c815e9aeac19553e0cb42be35c6a232264df766c511fb1d66acb942641c90" + }, + "controls/AUTHORING-STATUS.md": { + "bytes": 1545, + "sha256": "30e2d873de077fc890efe3d8dd407f0fbafb069f450478f2b35662a0d4f3b9a2" + }, + "controls/dispatch_control.c.txt": { + "bytes": 5160, + "sha256": "c87edf3865eb631c9b7c289fd163572f4a67dbcc286b079d55f92fa5cddc3364" + }, + "evidence/BINARY-PIN.json": { + "bytes": 270, + "sha256": "584daec6d2853deda5460d95bc41b513f8d7140ba2011106ad0dfe6568940792" + }, + "evidence/FINAL-RESULT.json": { + "bytes": 2711, + "sha256": "8e9ab1892788dd5ecf5808bc0a024c9cf04188dd1c05d05b41cde7b5900340b9" + }, + "evidence/LEDGER-001.json": { + "bytes": 561, + "sha256": "dcf67ced06c8223ee3c45237a9872ef72db246bf2c8a24183935e13d3ed8988e" + }, + "evidence/PARENT-ADMISSION.json": { + "bytes": 1523, + "sha256": "1d822ab0bb7fbbca3ce42bdf3a47cd0e5ef8315321308d381477bc8643041e64" + }, + "evidence/PREDECESSOR-WARNING-STATEMENTS.json": { + "bytes": 444, + "sha256": "6b45081a359a41dd39b95e61601c8abed0a5701f2cef05751847bbebf78a244d" + }, + "evidence/compile/RESULT.json": { + "bytes": 762, + "sha256": "c74304533416bbc0ad160e04b35c65b7a5f229c1172035115a6ede9b8e6a8105" + }, + "evidence/compile/START.json": { + "bytes": 658, + "sha256": "50f34f7d3e44f3c20d449c9c29f848a9255a95cf33f7dcf879a67a93edbb1522" + }, + "evidence/compile/stderr.raw": { + "bytes": 3252, + "sha256": "e7a2833e67848b3d895447c4923b6ff96f22a96ca0a94b49a13f84d3aae1b0c0" + }, + "evidence/compile/stdout.raw": { + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "evidence/mock-control/RESULT.json": { + "bytes": 777, + "sha256": "ddc2d7a33fbe8e444999c8e23e1aa003c9b0e07ad614a0ee2bbe75f26ac4764c" + }, + "evidence/mock-control/START.json": { + "bytes": 331, + "sha256": "66e460447882f62a05c7459f4f1ee222b4ed557ba910bdca2b3133d8d304e62c" + }, + "evidence/mock-control/stderr.raw": { + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "evidence/mock-control/stdout.raw": { + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "evidence/run_mock_guard.py.txt": { + "bytes": 4818, + "sha256": "84dd4ee39e68d38bb030b4b40330bd63b2dc52e6bd6e9de18c622885ba643f0e" + }, + "review/CONSEQUENCE-REVIEW-v1.md": { + "bytes": 17345, + "sha256": "804abed4b9e5223cba5bb0d1991315f0bc0d892f62c1a71e7b92a0e07fb8a583" + }, + "review/MOCK-CONTROL-ADMISSION-v1.md": { + "bytes": 2926, + "sha256": "2c6badd3e55164463dfa25a90c5ac1fa1f661aad06d709aed54191deb7cf1426" + }, + "review/MOCK-RESULT-ACCEPTANCE-v1.md": { + "bytes": 3261, + "sha256": "bb49c9cbb99c8c37666959d05534ba78ff9e76e63d8e0f7db369d991361f8d14" + }, + "review/NATIVE-PREREQUISITE-REVIEW-v1.md": { + "bytes": 4016, + "sha256": "b337c15e8073efbd8e9d552e10fe3b405fa4f84085238eec3b576347aa1361ea" + }, + "source/VIEW-ENTRY-CONTRACT-v1.md": { + "bytes": 27645, + "sha256": "39c17afc3f0a9981e659c69cc0513be71fb95630f67c5419520e7abc423f1185" + }, + "source/native-profile.json": { + "bytes": 3095, + "sha256": "d34bf38d322ad3a4331b13aac415ad181c2bb328314d2e8d4e71b6fb975387ef" + }, + "source/science_view_entry.c.txt": { + "bytes": 45415, + "sha256": "60af72915efea62f46ea376e2ba7ed0c616f99b09ca1beec0f4b0d8173609410" + }, + "verify_integrity.py": { + "bytes": 984, + "sha256": "08a6b3615ae19f61dd2ea58172d22b539cc2ef79ffa25e3635b71d6505e0fdf5" + } + }, + "scope": "Exact inert source snapshots, independent reviews, actual mocked control evidence and passive byte checker only" +} diff --git a/research/h25-private-binary-dispatch/PROVENANCE.json b/research/h25-private-binary-dispatch/PROVENANCE.json new file mode 100644 index 0000000000..d3057b6416 --- /dev/null +++ b/research/h25-private-binary-dispatch/PROVENANCE.json @@ -0,0 +1,94 @@ +{ + "schema": "private-binary-dispatch-provenance-v1", + "base_commit": "7c49fe32c55de02c2c873655c804b16e4c3fef0a", + "original_PR": "https://github.com/CrocSwap/integer-mult-bounds/pull/6321", + "direct_predecessor": { + "PR": 6818, + "head": "1479ccf2ec0bc29b7bf741a2d613d79e1f9ec19e", + "path": "research/h25-original-refusal-repair/snapshots/implementation/native/science_view_entry.c.txt", + "sha256": "14aa284e4671bc454b58b37fa9f4884d8c72774cff42a34b05d87991df2a121c" + }, + "coverage": { + "schema": "native-dispatch-upstream-coverage-v1", + "observed_utc": "2026-11-06 22:10:56 UTC", + "main": "7c49fe32c55de02c2c873655c804b16e4c3fef0a", + "predecessor": "outputs/caller-refusal-repair-20261106T2119Z/control/PR-COVERAGE-v1.json", + "decisions": [ + { + "number": 6715, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/6715", + "head": "dc34598c22e2ef43ae7e11fc1d8c14de3915cf89", + "state": "open", + "draft": true, + "decision": "reuse", + "reason": "Exact accepted complete scientific/native dependency source selection; no new image substitution." + }, + { + "number": 6736, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/6736", + "head": "0f2a86fa9ef0daf760b77c662a5b762e17848598", + "state": "open", + "draft": true, + "decision": "reuse", + "reason": "Original genuine parent ownership, fd/error/retention mechanics remain controlling; image/profile hashes do not prove new dispatch consequences." + }, + { + "number": 6744, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/6744", + "head": "1ac58a84b725bd6f17eec66727edc877c49c7e66", + "state": "open", + "draft": true, + "decision": "reuse", + "reason": "Original complete scientific subjects and original activity/caller criteria remain byte-exact." + }, + { + "number": 6762, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/6762", + "head": "3bcf99910a95248452e955d382c59bd23792a39b", + "state": "open", + "draft": true, + "decision": "review-only", + "reason": "Later CLOSE component remains separate from current native/source closure." + }, + { + "number": 6780, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/6780", + "head": "a7903f7ee16c85015f3f9a87fd6f3ae5c9053349", + "state": "open", + "draft": true, + "decision": "review-only", + "reason": "Original metadata enforcement finding remains a proof obligation; no Landlock-only fallback." + }, + { + "number": 6818, + "url": "https://github.com/CrocSwap/integer-mult-bounds/pull/6818", + "head": "1479ccf2ec0bc29b7bf741a2d613d79e1f9ec19e", + "state": "open", + "draft": true, + "decision": "adapt", + "reason": "Exact published corrected runner/profile/source graph is predecessor; carry its accepted startup refusal controls unchanged." + } + ], + "queries": [ + { + "query": "repo:CrocSwap/integer-mult-bounds is:pr \"binfmt_misc\"", + "total": 0, + "incomplete": false, + "ids": [] + }, + { + "query": "repo:CrocSwap/integer-mult-bounds is:pr \"FSCONFIG_CMD_CREATE\"", + "total": 0, + "incomplete": false, + "ids": [] + } + ], + "new_code_reason": "No concrete equivalent repair was identified by the bounded exact-behavior title/body searches. Actual published entry lacks fs-context/dispatch ownership; adapt its existing source owners rather than another wrapper.", + "coverage_limit": "All-author/all-state bounded PR metadata searches and exact implicated heads; not global source absence. Exact original source and primary Linux function review remain required.", + "no_upstream_or_source_qualification_inferred_from_merge_or_CI": true + }, + "new_native_execution": false, + "complete_original_caller_accepted": false, + "new_original_credit": 0, + "physical_costs": "OPEN_NOT_ZERO" +} diff --git a/research/h25-private-binary-dispatch/README.md b/research/h25-private-binary-dispatch/README.md new file mode 100644 index 0000000000..1a31cd124f --- /dev/null +++ b/research/h25-private-binary-dispatch/README.md @@ -0,0 +1,41 @@ +# Private binary-format dispatch ownership + +The proposed native entry now creates an empty `binfmt_misc` instance in its fresh user namespace before building its filesystem view, dropping privileges or starting a descendant. This shadows inherited miscellaneous binary handlers, including handlers that retain an interpreter file opened before the new filesystem view existed. + +The entry reserves a descriptor slot before `fsopen`, records the actual descriptor immediately, calls `fsconfig(FSCONFIG_CMD_CREATE)`, and retires the context exactly once. A create error remains primary if close also fails. An uncertain close stops setup and is never retried. No visible registry mount is created. Existing descriptor, error and report caps remain unchanged. The final syscall allowlist continues to deny filesystem-context creation, mount changes, namespace creation, registration writes and descriptor import. + +This is a proposed source repair with independently reviewed source and eight passing mock-control groups. The actual entry source was compiled inside a harness whose main reaches only the new owner and its real descriptor/error machinery. Syscall and close boundaries were mocked. The production entry main, namespaces, handler operations and scientific caller were not executed. Complete caller acceptance and any measured optimization remain open. + +## Selected source premises + +The source profile selects the Linux v6.12 semantics and `CONFIG_BINFMT_MISC=y` with successful filesystem registration. This avoids module-request delegation during `fsopen`. It is a prospective selection, not an observation about the current host. The empty namespace instance is published during filesystem creation and survives disposal of its unattached superblock until user-namespace destruction. A create operation can have partially published state before a later security-hook error; such a failure terminates setup and cannot be called an original scientific refusal. + +Primary sources: [miscellaneous binary loading and instance creation](https://github.com/torvalds/linux/blob/v6.12/fs/binfmt_misc.c), [filesystem-context operations](https://github.com/torvalds/linux/blob/v6.12/fs/fsopen.c), [context release](https://github.com/torvalds/linux/blob/v6.12/fs/fs_context.c), [filesystem lookup and module request](https://github.com/torvalds/linux/blob/v6.12/fs/filesystems.c), and [namespace disposal](https://github.com/torvalds/linux/blob/v6.12/kernel/user_namespace.c). + +## Lineage and exact scope + +Original investigation: [PR #6321](https://github.com/CrocSwap/integer-mult-bounds/pull/6321). Original full caller and criteria: [PR #6744](https://github.com/CrocSwap/integer-mult-bounds/pull/6744) at `1ac58a84b725bd6f17eec66727edc877c49c7e66`. Direct predecessor: [PR #6818](https://github.com/CrocSwap/integer-mult-bounds/pull/6818) at `1479ccf2ec0bc29b7bf741a2d613d79e1f9ec19e`. + +The predecessor owns the corrected original optimized-refusal classification and connected caller sources. This package changes its existing native entry; it does not add another caller or replace original scientific bodies. The exact native delta is included. The original fixed helper and context coverage, resource limits, normal output oracle and optimized refusal oracle are preserved. The predecessor's accepted refusal controls were retained and were not rerun for this repair. + +Related work is reused at its exact recorded scope: [native selection #6715](https://github.com/CrocSwap/integer-mult-bounds/pull/6715), [parent ownership #6736](https://github.com/CrocSwap/integer-mult-bounds/pull/6736), [metadata consequence audit #6780](https://github.com/CrocSwap/integer-mult-bounds/pull/6780), and [later close component #6762](https://github.com/CrocSwap/integer-mult-bounds/pull/6762). The provenance record lists inspected commits and concrete reuse decisions. Bounded all-author, all-state PR title/body searches found no `binfmt_misc` or `FSCONFIG_CMD_CREATE` counterpart; this does not prove global code novelty. + +## Reproduction and evidence boundaries + +`source/science_view_entry.c.txt` is an inert exact source snapshot. The contract and profile describe its selected semantics. The original authoring-status file preserves the controls' initially unexecuted state. The later actual evidence records the sole admitted compile and mock run: both returned zero; the mock run had empty stdout and stderr. All eight fixed control groups passed. Compilation retained three pre-existing `-Wmisleading-indentation` warnings (3,252 bytes), not a warning-free result. The command used `-Wall -Wextra -Werror=implicit-function-declaration`. `review/` records independent source findings and their exact inputs. The guard, exact compiler/source identities, raw captures and results are retained under `evidence/`. The conservative policy charge was 24 seconds, including compile and mock-run allowances; full physical costs remain open. No replay registration is supplied for this unqualified production native source. + +After obtaining this package, run `python3 -B research/h25-private-binary-dispatch/verify_integrity.py` to check package membership and hashes only. This command neither imports the native caller nor executes its controls. Full caller dependencies remain in the exact predecessor package and its explicit external-input map; this contribution does not claim a self-contained cold science replay. + +## Open next questions + +1. **Required execution behavior.** Prove that removing inherited miscellaneous handlers preserves each original GNU-time, Python, compiler/helper, script and failed-exec observation. Exact executable bytes do not establish which handler would have run. The smallest next check is a finite source-selected dispatch map joined to the genuine parent association; it is blocked by the absent consequence owner. +2. **Fault results.** Establish the affected original fault observations under the entry's existing `CORE(1,1)` profile, including GNU-time diagnostics and compiler-child wait status. A Python signal return code alone omits core-status consequences. This requires the actual consumer chain, not an assumed current host setting. +3. **Creation and filesystem effects.** Join exact topology, permission and LSM creation/exec consequences to the same original caller. Subject hashes and metadata equality alone do not prove the new confinement preserves those consequences. Current kernel/images, actual association, numerical fit, funding and execution follow only after complete source acceptance. + +A helper or a delivered PR does not satisfy these complete-caller obligations. No performance claim, original full-data equality, supplier qualification or complete physical cost account is made. Preparation, consultation, agent review, reporting, publication and historical timing gaps remain `OPEN_NOT_ZERO`. + +## Credit + +Prepared for Clear1nsight with substantial OpenAI Codex and ChatGPT Pro assistance. Retains the predecessor native entry and its ownership, error and confinement machinery. Contributions are under the repository's Apache-2.0 license. Independent AI source review is distinct from human review, executed controls and runtime qualification. + +To reproduce just the mocked owner check, restore the two inert sources into a temporary directory with their original relative layout: `implementation/native/science_view_entry.c` and `controls/native-dispatch/dispatch_control.c`. Compile the control file using the recorded GCC command and then run that control executable under appropriate local limits. Its include must resolve to the exact supplied entry bytes. Do not run the production entry as this control. Current-host kernel behavior and complete caller qualification are outside this command. diff --git a/research/h25-private-binary-dispatch/controls/AUTHORING-STATUS.md b/research/h25-private-binary-dispatch/controls/AUTHORING-STATUS.md new file mode 100644 index 0000000000..bde52af41f --- /dev/null +++ b/research/h25-private-binary-dispatch/controls/AUTHORING-STATUS.md @@ -0,0 +1,7 @@ +# Native dispatch source controls — unexecuted + +`dispatch_control.c` includes the actual entry source and redirects only `syscall` and `close` to fixed mocks. It exercises the real `create_private_binfmt_dispatch`, reservation/acquisition registry, once-close transitions and error custody. It does not reproduce the owner as a separate policy implementation. + +Cases cover successful one acquisition/create/close; prohibited second invocation; failed fsopen with no create/close; raw CREATE failure; publication followed by CREATE failure and an uncertain close; successful CREATE with failed close; ordering violations; and FD-capacity exhaustion before syscall. The close mock checks RETIRED before the call and actual create errno already captured before a secondary close error. A later close_all must not retry an uncertain descriptor. The partial-publication mock deliberately keeps publication present when the syscall fails, so the native success flag cannot be interpreted as proof of absence. + +These are authored controls only. **No control, compiler, native executable, namespace, mount, filesystem context, host registration or kernel probe has run.** Compilation or execution requires independent source GO followed by explicit parent guarded admission under the unchanged budget. A passing mocked control would establish only these source-state/error transitions; Linux callback/lifetime semantics, built-in registry association, finite original exec/CORE/LSM/topology compatibility and whole S10.03 remain separate obligations. diff --git a/research/h25-private-binary-dispatch/controls/dispatch_control.c.txt b/research/h25-private-binary-dispatch/controls/dispatch_control.c.txt new file mode 100644 index 0000000000..c4f889c48d --- /dev/null +++ b/research/h25-private-binary-dispatch/controls/dispatch_control.c.txt @@ -0,0 +1,92 @@ +/* UNEXECUTED source controls. Compile/run only after independent source GO + * and the parent's guarded component admission. Includes the ACTUAL owner; + * mocks only native syscall/close boundaries, never namespaces or kernel state. + * These controls cannot establish Linux semantics or runtime compatibility. */ +#define syscall dispatch_mock_syscall +#define close dispatch_mock_close +#define main dispatch_unused_native_main +#include "../../implementation/native/science_view_entry.c" +#undef main +#undef close +#undef syscall +#include + +static int failures,opens,creates,closes,open_error,create_error,close_error; +static bool publish_then_fail,published; +static void check(bool value,const char *site) { + if(!value){fprintf(stderr,"control failure: %s\n",site);failures++;} +} +long dispatch_mock_syscall(long nr,...) { + va_list ap;va_start(ap,nr);long result=-1; + if(nr==SYS_fsopen) { + const char *name=va_arg(ap,const char *);unsigned flags=va_arg(ap,unsigned); + opens++; + check(fd_count==1&&fds[0].state==SV_PENDING,"reserve precedes fsopen"); + check(dispatch_attempted&&user_entered&&mount_entered&&root_private,"fresh setup owns call"); + check(!strcmp(name,"binfmt_misc")&&flags==1U,"exact fsopen subject and CLOEXEC"); + check(!root_readonly&&!landlocked&&!filtered,"setup precedes view and lockdown"); + if(open_error)errno=open_error;else result=7; + } else if(nr==SYS_fsconfig) { + int fd=va_arg(ap,int);unsigned command=va_arg(ap,unsigned); + void *key=va_arg(ap,void *),*value=va_arg(ap,void *);int auxiliary=va_arg(ap,int); + creates++; + check(opens==1&&fd==7&&command==6U&&!key&&!value&&auxiliary==0,"exact once-only CREATE"); + check(fds[0].state==SV_OPEN&&fds[0].fd==7,"acquire visible before CREATE"); + if(publish_then_fail||!create_error)published=true; + if(create_error)errno=create_error;else result=0; + } else {check(false,"unexpected native syscall");errno=ENOSYS;} + va_end(ap);return result; +} +int dispatch_mock_close(int fd) { + closes++; + check(fd==7&&fds[0].state==SV_RETIRED,"retirement precedes one close"); + if(create_error)check(error_count==1&&errors[0].number==create_error&& + !strcmp(errors[0].site,"private-binfmt-fsconfig-create"),"raw create primary precedes close"); + if(close_error){errno=close_error;return -1;}return 0; +} +static void reset_control(void) { + memset(fds,0,sizeof fds);memset(errors,0,sizeof errors); + fd_count=error_count=0;uncertain=error_overflow=false; + user_entered=mount_entered=root_private=true; + root_readonly=landlocked=filtered=false; + dispatch_attempted=dispatch_created=dispatch_context_closed=false; + opens=creates=closes=open_error=create_error=close_error=0; + publish_then_fail=published=false;errno=0; +} +int main(void) { + reset_control(); + check(create_private_binfmt_dispatch(),"successful setup result"); + check(opens==1&&creates==1&&closes==1&&published,"one acquisition/create/retirement"); + check(dispatch_created&&dispatch_context_closed&&fds[0].state==SV_CLOSED&&!uncertain&&!error_count,"success states"); + check(!create_private_binfmt_dispatch()&&opens==1&&creates==1&&closes==1,"duplicate invocation blocked"); + + reset_control();open_error=ENODEV; + check(!create_private_binfmt_dispatch(),"fsopen failure stops"); + check(opens==1&&!creates&&!closes&&fds[0].state==SV_FAILED&&error_count==1&&errors[0].number==ENODEV,"negative acquisition has no close or create"); + + reset_control();create_error=EACCES; + check(!create_private_binfmt_dispatch(),"CREATE failure stops"); + check(opens==1&&creates==1&&closes==1&&!dispatch_created&&dispatch_context_closed&& + error_count==1&&errors[0].number==EACCES&&fds[0].state==SV_CLOSED,"failed create retains actual primary"); + + reset_control();create_error=EACCES;close_error=EINTR;publish_then_fail=true; + check(!create_private_binfmt_dispatch(),"partial-publication plus uncertain close stops"); + check(published&&!dispatch_created&&!dispatch_context_closed&&uncertain&& + error_count==2&&errors[0].number==EACCES&&errors[1].number==EINTR&& + !strcmp(errors[1].site,"close-once")&&fds[0].state==SV_UNCERTAIN,"partial publication not declared absent or retired"); + close_all();check(closes==1,"uncertain close is never retried"); + + reset_control();close_error=EIO; + check(!create_private_binfmt_dispatch()&&dispatch_created&&!dispatch_context_closed&&uncertain,"successful publication plus close failure stops"); + close_all();check(closes==1&&error_count==1&&errors[0].number==EIO,"single close error retained without retry"); + + reset_control();mount_entered=false; + check(!create_private_binfmt_dispatch()&&!opens&&!creates&&!closes,"wrong namespace ordering blocks native call"); + + reset_control();root_readonly=true; + check(!create_private_binfmt_dispatch()&&!opens,"late setup blocked"); + + reset_control();fd_count=SV_MAX_FDS; + check(!create_private_binfmt_dispatch()&&!opens&&error_count==1&&errors[0].number==EMFILE,"fixed FD capacity stops before acquisition"); + return failures?1:0; +} diff --git a/research/h25-private-binary-dispatch/evidence/BINARY-PIN.json b/research/h25-private-binary-dispatch/evidence/BINARY-PIN.json new file mode 100644 index 0000000000..151db86e94 --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/BINARY-PIN.json @@ -0,0 +1,5 @@ +{ + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/dispatch_control", + "bytes": 53800, + "sha256": "d9f508da6505884baa148b6abe078c0528410ce9b85bea8ee0c74ccb08cc24b5" +} diff --git a/research/h25-private-binary-dispatch/evidence/FINAL-RESULT.json b/research/h25-private-binary-dispatch/evidence/FINAL-RESULT.json new file mode 100644 index 0000000000..f1e6387492 --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/FINAL-RESULT.json @@ -0,0 +1,65 @@ +{ + "status": "PASS", + "steps": [ + { + "step": "compile", + "returncode": 0, + "error": null, + "group_kill": false, + "wall_seconds": 0.3978756439755671, + "captures": { + "stdout": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/compile/stdout.raw", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "stderr": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/compile/stderr.raw", + "bytes": 3252, + "sha256": "e7a2833e67848b3d895447c4923b6ff96f22a96ca0a94b49a13f84d3aae1b0c0" + } + }, + "complete": true + }, + { + "step": "mock-control", + "returncode": 0, + "error": null, + "group_kill": false, + "wall_seconds": 0.0022189459996297956, + "captures": { + "stdout": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/mock-control/stdout.raw", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "stderr": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/mock-control/stderr.raw", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + "complete": true + } + ], + "compiler": { + "path": "/usr/bin/x86_64-linux-gnu-gcc-13", + "bytes": 1023032, + "sha256": "1b99826121ae6682a634e5efe09bd3e3df58ce58e0b28f849114ab5b89139c26" + }, + "native_source": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/implementation/native/science_view_entry.c", + "bytes": 45415, + "sha256": "60af72915efea62f46ea376e2ba7ed0c616f99b09ca1beec0f4b0d8173609410" + }, + "control_source": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/dispatch_control.c", + "bytes": 5160, + "sha256": "c87edf3865eb631c9b7c289fd163572f4a67dbcc286b079d55f92fa5cddc3364" + }, + "case_groups": 8, + "production_native_entry_executed": false, + "actual_namespace_or_handler_probe": false, + "complete_caller_execution": false, + "original_credit_delta": 0 +} diff --git a/research/h25-private-binary-dispatch/evidence/LEDGER-001.json b/research/h25-private-binary-dispatch/evidence/LEDGER-001.json new file mode 100644 index 0000000000..f94ed2500c --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/LEDGER-001.json @@ -0,0 +1,15 @@ +{ + "schema": "guarded-native-mock-policy-ledger-v1", + "baseline_policy_seconds": "1432.890533479", + "new_conservative_debit_seconds": "24", + "combined_policy_seconds": "1456.890533479", + "remaining_policy_seconds": "943.109466521", + "cap_seconds": 2400, + "reserved_outstanding_seconds": 0, + "attempt_steps": 2, + "outer_wall_seconds": 0.40423043695045635, + "new_budget": false, + "no_refunds": true, + "physical_costs": "OPEN_NOT_ZERO", + "outside_preparation_Pro_agents_review_reporting_publication_final_metadata_historical_costs": "OPEN_NOT_ZERO" +} diff --git a/research/h25-private-binary-dispatch/evidence/PARENT-ADMISSION.json b/research/h25-private-binary-dispatch/evidence/PARENT-ADMISSION.json new file mode 100644 index 0000000000..ae0a59a039 --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/PARENT-ADMISSION.json @@ -0,0 +1,22 @@ +{ + "scope": "one guarded compile of actual-owner mock and one mocked run only after compile success", + "native_source_sha256": "60af72915efea62f46ea376e2ba7ed0c616f99b09ca1beec0f4b0d8173609410", + "control_sha256": "c87edf3865eb631c9b7c289fd163572f4a67dbcc286b079d55f92fa5cddc3364", + "compiler": { + "path": "/usr/bin/x86_64-linux-gnu-gcc-13", + "sha256": "1b99826121ae6682a634e5efe09bd3e3df58ce58e0b28f849114ab5b89139c26" + }, + "guard": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/validation/run_mock_guard_v3.py", + "sha256": "84dd4ee39e68d38bb030b4b40330bd63b2dc52e6bd6e9de18c622885ba643f0e" + }, + "existing_source_GO": "review/whole/MOCK-CONTROL-ADMISSION-v1.json", + "policy_reservation_seconds": 24, + "baseline_policy_seconds": "1432.890533479", + "cap_seconds": 2400, + "execution_of_production_native_entry_GO": false, + "parent_admission": "GO for one guarded compilation and mocked actual-owner run only; source-reviewed interfaces, fixed limits and24second conservative debit", + "native_scientific_execution_GO": false, + "compile_warning_policy": "Retain -Wall/-Wextra diagnostics. Fatal implicit declarations. Existing full-entry style warnings are not the state-machine oracle and are not silently discarded.", + "pre_attempt_reconciliation": "Independent reviewer identified command-only warning policy and exact compiler prebirth hash check; v1/v2 remain unexecuted; v3 is sole admitted attempt." +} diff --git a/research/h25-private-binary-dispatch/evidence/PREDECESSOR-WARNING-STATEMENTS.json b/research/h25-private-binary-dispatch/evidence/PREDECESSOR-WARNING-STATEMENTS.json new file mode 100644 index 0000000000..23ecdd4cbd --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/PREDECESSOR-WARNING-STATEMENTS.json @@ -0,0 +1,9 @@ +{ + "predecessor_sha256": "14aa284e4671bc454b58b37fa9f4884d8c72774cff42a34b05d87991df2a121c", + "unchanged_statement_bytes": [ + "if(fstat(fd,out)<0)return failed(site);return true;", + "if(syscall(SYS_landlock_add_rule,rules,1,&path,0)<0)return failed(site);return true;", + "if(n==0)break;at+=(size_t)n;" + ], + "scope": "Current compiler emitted warnings on these unchanged predecessor statements; no predecessor compilation rerun." +} diff --git a/research/h25-private-binary-dispatch/evidence/compile/RESULT.json b/research/h25-private-binary-dispatch/evidence/compile/RESULT.json new file mode 100644 index 0000000000..714e9cb3df --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/compile/RESULT.json @@ -0,0 +1,20 @@ +{ + "step": "compile", + "returncode": 0, + "error": null, + "group_kill": false, + "wall_seconds": 0.3978756439755671, + "captures": { + "stdout": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/compile/stdout.raw", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "stderr": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/compile/stderr.raw", + "bytes": 3252, + "sha256": "e7a2833e67848b3d895447c4923b6ff96f22a96ca0a94b49a13f84d3aae1b0c0" + } + }, + "complete": true +} diff --git a/research/h25-private-binary-dispatch/evidence/compile/START.json b/research/h25-private-binary-dispatch/evidence/compile/START.json new file mode 100644 index 0000000000..39c9e08f46 --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/compile/START.json @@ -0,0 +1,19 @@ +{ + "state": "BIRTH_PENDING", + "argv": [ + "/usr/bin/x86_64-linux-gnu-gcc-13", + "-std=c11", + "-O0", + "-Wall", + "-Wextra", + "-Werror=implicit-function-declaration", + "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/dispatch_control.c", + "-o", + "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/dispatch_control" + ], + "wall_seconds": 15, + "CPU_seconds": 10, + "AS_bytes": 536870912, + "FSIZE_bytes": 1048576, + "capture_cap_each": 65536 +} diff --git a/research/h25-private-binary-dispatch/evidence/compile/stderr.raw b/research/h25-private-binary-dispatch/evidence/compile/stderr.raw new file mode 100644 index 0000000000..8a352cf001 --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/compile/stderr.raw @@ -0,0 +1,22 @@ +In file included from /home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/dispatch_control.c:8: +/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/../../implementation/native/science_view_entry.c: In function 'stat_fd': +/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/../../implementation/native/science_view_entry.c:189:5: warning: this 'if' clause does not guard... [-Wmisleading-indentation] + 189 | if(fstat(fd,out)<0)return failed(site);return true; + | ^~ +/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/../../implementation/native/science_view_entry.c:189:44: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if' + 189 | if(fstat(fd,out)<0)return failed(site);return true; + | ^~~~~~ +/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/../../implementation/native/science_view_entry.c: In function 'll_rule': +/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/../../implementation/native/science_view_entry.c:518:5: warning: this 'if' clause does not guard... [-Wmisleading-indentation] + 518 | if(syscall(SYS_landlock_add_rule,rules,1,&path,0)<0)return failed(site);return true; + | ^~ +/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/../../implementation/native/science_view_entry.c:518:77: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if' + 518 | if(syscall(SYS_landlock_add_rule,rules,1,&path,0)<0)return failed(site);return true; + | ^~~~~~ +/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/../../implementation/native/science_view_entry.c: In function 'report_failure': +/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/../../implementation/native/science_view_entry.c:668:9: warning: this 'if' clause does not guard... [-Wmisleading-indentation] + 668 | if(n==0)break;at+=(size_t)n; + | ^~ +/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/controls/native-dispatch/../../implementation/native/science_view_entry.c:668:23: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if' + 668 | if(n==0)break;at+=(size_t)n; + | ^~ diff --git a/research/h25-private-binary-dispatch/evidence/compile/stdout.raw b/research/h25-private-binary-dispatch/evidence/compile/stdout.raw new file mode 100644 index 0000000000..e69de29bb2 diff --git a/research/h25-private-binary-dispatch/evidence/mock-control/RESULT.json b/research/h25-private-binary-dispatch/evidence/mock-control/RESULT.json new file mode 100644 index 0000000000..c08bb6f81c --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/mock-control/RESULT.json @@ -0,0 +1,20 @@ +{ + "step": "mock-control", + "returncode": 0, + "error": null, + "group_kill": false, + "wall_seconds": 0.0022189459996297956, + "captures": { + "stdout": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/mock-control/stdout.raw", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "stderr": { + "path": "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/mock-control/stderr.raw", + "bytes": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + "complete": true +} diff --git a/research/h25-private-binary-dispatch/evidence/mock-control/START.json b/research/h25-private-binary-dispatch/evidence/mock-control/START.json new file mode 100644 index 0000000000..92d3fc6a79 --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/mock-control/START.json @@ -0,0 +1,11 @@ +{ + "state": "BIRTH_PENDING", + "argv": [ + "/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453/outputs/native-dispatch-closure-20261106T2206Z/execution-mock/dispatch_control" + ], + "wall_seconds": 2, + "CPU_seconds": 1, + "AS_bytes": 536870912, + "FSIZE_bytes": 1048576, + "capture_cap_each": 65536 +} diff --git a/research/h25-private-binary-dispatch/evidence/mock-control/stderr.raw b/research/h25-private-binary-dispatch/evidence/mock-control/stderr.raw new file mode 100644 index 0000000000..e69de29bb2 diff --git a/research/h25-private-binary-dispatch/evidence/mock-control/stdout.raw b/research/h25-private-binary-dispatch/evidence/mock-control/stdout.raw new file mode 100644 index 0000000000..e69de29bb2 diff --git a/research/h25-private-binary-dispatch/evidence/run_mock_guard.py.txt b/research/h25-private-binary-dispatch/evidence/run_mock_guard.py.txt new file mode 100644 index 0000000000..c17d9d96d4 --- /dev/null +++ b/research/h25-private-binary-dispatch/evidence/run_mock_guard.py.txt @@ -0,0 +1,63 @@ +"""Parent-owned bounded compile and mocked actual-owner control, one attempt each.""" +from pathlib import Path +import hashlib,json,os,resource,selectors,signal,subprocess,time,datetime +from decimal import Decimal +W=Path('/home/d3lerium/.codex/.chatgpt-projects/g-p-6ad5817fd84881918865926b6fd51453') +R=W/'outputs/native-dispatch-closure-20261106T2206Z' +D=R/'execution-mock';D.mkdir(exist_ok=False) +N=R/'implementation/native/science_view_entry.c';C=R/'controls/native-dispatch/dispatch_control.c' +assert hashlib.sha256(N.read_bytes()).hexdigest()=='60af72915efea62f46ea376e2ba7ed0c616f99b09ca1beec0f4b0d8173609410' +assert hashlib.sha256(C.read_bytes()).hexdigest()=='c87edf3865eb631c9b7c289fd163572f4a67dbcc286b079d55f92fa5cddc3364' +compiler=Path('/usr/bin/gcc').resolve(strict=True) +assert hashlib.sha256(compiler.read_bytes()).hexdigest()=='1b99826121ae6682a634e5efe09bd3e3df58ce58e0b28f849114ab5b89139c26' +def put(p,j): + with p.open('x')as f:json.dump(j,f,indent=2);f.write('\n') + p.chmod(0o444) +def pin(p):return {'path':str(p),'bytes':p.stat().st_size,'sha256':hashlib.sha256(p.read_bytes()).hexdigest()} +base=Decimal('1432.890533479');debit=Decimal('24');start=time.monotonic() +put(D/'LEDGER-000.json',{'baseline_policy_seconds':str(base),'reserved_outstanding_seconds':24,'cap_seconds':2400,'new_budget':False,'physical_costs':'OPEN_NOT_ZERO'}) +rows=[] +def run(name,argv,wall,cpu): + out=D/name;out.mkdir();begin=time.monotonic();put(out/'START.json',{'state':'BIRTH_PENDING','argv':argv,'wall_seconds':wall,'CPU_seconds':cpu,'AS_bytes':536870912,'FSIZE_bytes':1048576,'capture_cap_each':65536}) + def limits(): + resource.setrlimit(resource.RLIMIT_CPU,(cpu,cpu));resource.setrlimit(resource.RLIMIT_AS,(536870912,536870912));resource.setrlimit(resource.RLIMIT_FSIZE,(1048576,1048576));resource.setrlimit(resource.RLIMIT_CORE,(0,0));resource.setrlimit(resource.RLIMIT_NOFILE,(64,64)) + p=None;error=None;killed=False;chunks={'stdout':bytearray(),'stderr':bytearray()};sel=selectors.DefaultSelector() + try: + p=subprocess.Popen(argv,cwd=str(W),env={'PATH':'/usr/bin:/bin','LC_ALL':'C','LANG':'C'},stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,start_new_session=True,close_fds=True,preexec_fn=limits) + for key,pipe in [('stdout',p.stdout),('stderr',p.stderr)]:os.set_blocking(pipe.fileno(),False);sel.register(pipe,selectors.EVENT_READ,key) + end=begin+wall + while sel.get_map(): + remain=end-time.monotonic() + if remain<=0:raise TimeoutError('wall deadline') + for key,event in sel.select(min(remain,0.1)): + block=os.read(key.fileobj.fileno(),min(4096,65537-len(chunks[key.data]))) + if not block:sel.unregister(key.fileobj);continue + chunks[key.data].extend(block) + if len(chunks[key.data])>65536:raise RuntimeError('capture overflow') + p.wait(timeout=max(0.001,end-time.monotonic())) + except BaseException as e: + error=type(e).__name__+': '+str(e) + if p is not None and p.returncode is None: + try:os.killpg(p.pid,signal.SIGKILL);killed=True + except ProcessLookupError:pass + p.wait(timeout=2) + finally: + for key in list(sel.get_map().values()):sel.unregister(key.fileobj) + sel.close() + if p is not None: + for pipe in (p.stdout,p.stderr): + if pipe is not None:pipe.close() + pins={} + for key,value in chunks.items(): + q=out/(key+'.raw');q.write_bytes(value);q.chmod(0o444);pins[key]=pin(q) + row={'step':name,'returncode':None if p is None else p.returncode,'error':error,'group_kill':killed,'wall_seconds':time.monotonic()-begin,'captures':pins,'complete':error is None and p is not None and p.returncode==0} + put(out/'RESULT.json',row);rows.append(row) + return row['complete'] +binary=D/'dispatch_control' +try: + okay=run('compile',[str(compiler),'-std=c11','-O0','-Wall','-Wextra','-Werror=implicit-function-declaration',str(C),'-o',str(binary)],15,10) + if okay: + binary.chmod(0o500);put(D/'BINARY-PIN.json',pin(binary));okay=run('mock-control',[str(binary)],2,1) + put(D/'FINAL-RESULT.json',{'status':'PASS' if okay else 'FAILED_NO_RETRY','steps':rows,'compiler':pin(compiler),'native_source':pin(N),'control_source':pin(C),'case_groups':8,'production_native_entry_executed':False,'actual_namespace_or_handler_probe':False,'complete_caller_execution':False,'original_credit_delta':0}) +finally: + put(D/'LEDGER-001.json',{'schema':'guarded-native-mock-policy-ledger-v1','baseline_policy_seconds':str(base),'new_conservative_debit_seconds':str(debit),'combined_policy_seconds':str(base+debit),'remaining_policy_seconds':str(Decimal(2400)-base-debit),'cap_seconds':2400,'reserved_outstanding_seconds':0,'attempt_steps':len(rows),'outer_wall_seconds':time.monotonic()-start,'new_budget':False,'no_refunds':True,'physical_costs':'OPEN_NOT_ZERO','outside_preparation_Pro_agents_review_reporting_publication_final_metadata_historical_costs':'OPEN_NOT_ZERO'}) diff --git a/research/h25-private-binary-dispatch/review/CONSEQUENCE-REVIEW-v1.md b/research/h25-private-binary-dispatch/review/CONSEQUENCE-REVIEW-v1.md new file mode 100644 index 0000000000..ffc3c02859 --- /dev/null +++ b/research/h25-private-binary-dispatch/review/CONSEQUENCE-REVIEW-v1.md @@ -0,0 +1,69 @@ +# Finite native consequences: focused source review + +**Decision: accept the newly implemented private-dispatch ownership edge at the inspected source scope; HOLD whole S10.03 acceptance.** The connected tuple still lacks a concrete finite original dispatch/LSM/topology/fault implication and the parent-side facts/validators needed to associate it. This is a source finding, not a demand for an actual namespace/kernel/image run before authoring. Current environment qualification and all execution remain unissued. + +Review target is the exact new source read on2026-11-06 before the fixed22:31:19Z source cutoff; exact hashes and line references are in `SOURCE-EVIDENCE-v1.json`. The author can still change owned source before that cutoff; a changed hash requires an exact reread before adopting this conclusion. Only this assigned review directory was written. No project import, test, compiler/native/scientific execution, archive decode or actual host/process/namespace/kernel/ACL probe occurred. Primary source web reads do not associate a current host. + +## Original criterion and retained source map + +Original S10.03 still requires assembling and independently reviewing the complete assertion-enabled candidate caller with complete original checks and exact output/error semantics. The finite original scope remains52 helper bodies in65 distinct contexts, plus original full/optimized/P08/absent/None/refusal/retired domains. The accepted catalogue is `outputs/s03-frozen-source-selection-20261104T1210Z/implementation/SOURCE-SELECTIONS-v1.json`, SHA256`97af356321e9dabe465c70f6a475e6b957735152d293359e9713cf01c7733d96`. `ORIGINAL-CONTEXT-MAP-v1.json` records every catalogue case/context/oracle/image pin without executing it. These identities were extracted as inert JSON, not imported or regenerated fixtures. + +The connected receiver `source/full_result_receiver.py`:24–36,47–59,67–98 selects the unchanged full candidate and optimized full refusal. Its111–166 checks the exact adopted catalogue, recipe and command inverse, including every unchanged stage member. This retains the source bodies. It does not independently execute65 context entries or prove that selecting two outer jobs reaches every required original context. Original source/control reachability must retain its accepted context-specific map; no body/context credit follows from two outer labels or a stage hash. + +## Implemented dispatch owner: useful positive source result + +`native/science_view_entry.c`:371–398 now owns a real reserved/acquired `fsopen("binfmt_misc",FSOPEN_CLOEXEC)` descriptor, issues `fsconfig(FSCONFIG_CMD_CREATE)` once, records the actual create error before close, and retires/closes once before proceeding. At702 it runs after fresh same-ID user/private mount namespaces and before copied-view construction, capability drop, Landlock, final seccomp or descendants. The final filter omits reconfiguration/namespace/mount/import operations from its allowed vocabulary. The contract85–89 explicitly keeps partial publication on create failure and namespace/process retirement; it does not interpret a false success flag as absence of a partially created instance. + +The updated native profile13–18 concretely selects Linuxv6.12 `CONFIG_BINFMT_MISC=y` and successful prior registration of the built-in implementation, so filesystem lookup need not request a module. This is a precise prospective operating-TCB selection; it is not current kernel evidence. The primary [Linuxv6.12 binfmt implementation](https://github.com/torvalds/linux/blob/v6.12/fs/binfmt_misc.c) supports namespace-local shadowing:169–183 stops at the first existing instance,938–953 initializes/publishes its empty list, and the subsequent empty-list dispatch returns ENOEXEC. The old F-interpreter reference is therefore no longer merely assumed absent after successful private-instance construction. + +This closes the specific **old-reference confinement ownership edge** conditionally on the selected interface semantics and successful setup. It does not prove that removing ancestor handlers preserves the original command's dispatch result. Those are different obligations. + +## Finite required exec and creation map + +| Reached class | Actual original source/consumer | Required concrete consequence fact | Inspected status | +|---|---|---|---| +| Final GNU time exec | Entry715 uses the original time argv; runner615 constructs exact `-f`/`-o` command; receiver requires exact original command | Original visible handler matching/order must leave this selected image on the same ordinary dispatch path; final ELF interpreter/loader opens must use the same final-view objects and permissions | Time bytes are pinned and rehashed; original matching handler state and its consequence are not associated | +| Original Python Root, normal and optimized | Receiver87–98 explicitly executes `/usr/bin/python3.12` with `-S -s -P -B` and optional `-O`; scientific source is an argument | Same Python/loader dispatch and exact flags/environment; original optimized first refusal remains its own Python body error | Correct command/source inverse retained; ELF/image pin alone does not exclude an earlier misc-handler match | +| Cold metadata compiler | `integration/native_packet.py`:17–105,180–244 pins compiler/dependency bytes and invokes the absolute GCC13 command; `cold_build_evidence.py`:169–240 performs genuine Popen/poll/wait | Complete executed driver/helper closure, ordinary dispatch for each actual executable, exact loader behavior, same permission and security-domain transitions | Original build checks retained; metadata role contract mandates eight Python/native paths, not the complete compiler/helper dispatch closure | +| GCC helpers and linking | Build closure includes `cc1`, `collect2`, assembler/linker images, plugin/CRT/library inputs | Same argv-derived helper selection, PATH/driver search, executable and loader dispatch; data/linker scripts remain data unless a selected helper actually execs them | Byte checks are useful but are not a proof of original matching handlers, policy effects or helper search semantics | +| Original backend and trace builds | `baseline/engine/native_backend.py`:268–276 and `integration/adapters.py`:53–66 derive compiler argv from selected sysconfig CC and use subprocess | Exact selected sysconfig CC, ordered PATH resolution, target/helper image closure and original timeout/return/raw-stream semantics | An absolute compiler in another build contract does not itself bind these two derived commands | +| Scientific Python scripts | Source files have shebangs, but reached full command passes them to explicit Python; helper controls use explicit Python `-c` | Keep this distinction: source text with a shebang is not evidence of a reached kernel script dispatch | No direct scientific shebang exec was established in the inspected paths; any actually reached compiler/helper script must separately map its interpreter chain | +| Genuine absent executable | `cold_build_evidence_controls.py`:78–90 returns an absent absolute path;149–151 requires actual FileNotFoundError, spawn-entered true/returned false and no returncode | Path lookup must still fail ENOENT before binary-format dispatch; no synthetic startup receipt or replacement exec | This finite missing-path implication is source-supported, assuming unchanged path topology/permission and no actor creates the path | +| Nonzero, timeout and oversize toy children | Controls78–87,124–155; helper218–240 | Explicit Python `-c`; nonzero7 is an exit, timeout retains no completed wait, oversized raw log retains its original log-limit failure | Finite declared consumer semantics are explicit; native policy must permit genuine spawn, writes, wait and cleanup | +| Live-thread/prelaunch negative | Controls104–118 starts a genuine thread;151–155 requires the original exactly-one-task refusal before Popen | The real thread fixture must be creatable/joinable with original proc/task observation; earlier seccomp/LSM clone failure is a different result | Thread clone tuple is permitted by native source, but installed libc/LSM dispatch and complete original fixture effect association remain separate | +| Compiler/scientific/certificate creation | Original scientific driver965–971 copies original frozen/engine sources, chmods only new runtime-root and makes certificates; compiler outputs remain under artifact | Concrete filesystem/LSM creation effects, inherited default/access ACL consequences, umask/mode and same kernel UID; private writable mount does not silently alter required creation/refusal | Metadata observer captures attrs/modes/credentials; current subject bytes/hash do not derive policy effects | + +For a positive finite original exec set, a sufficient dispatch premise is that every actual original visible enabled misc registration has no matching magic/mask/extension for each reached executable/interpreter, with the same result maintained during the interval; alternatively each matching route needs its own complete equality argument. The fresh private empty instance then contributes no replacement matching route, and the selected ordinary ELF/script dispatch can be compared. **No original handler-match vector, stable interval owner or selected equivalent dispatch policy is present in the current parent association schema.** Saying only “the images are ELF” or “the selected kernel has built-in misc” leaves this semantic edge open. If a required original case depends on a handler, its removal cannot be relabeled original refusal. + +## CORE: actual consumers and precise limits of the finding + +The entry341–347 installs/reads back `(1,1)` before its namespaces. Filter573–581 denies later CORE mutations, including lowering to0. The selected [Linuxv6.12 core source](https://github.com/torvalds/linux/blob/v6.12/fs/coredump.c) supplies the intended helper/file suppression slice; its `coredump_finish`414–422 sets the0x80 status bit only after a dump. This is suppression source reasoning, not equivalence of the caller's fault observations. + +**G's original CORE is not established.** The exact G source `retention_guardian.c`:506–527 prepares/installs AS and FSIZE only. `inner_meter.py`:499–500 likewise adjusts AS/FSIZE, and controller entry adjusts AS. A bounded read found no CORE installation in those inspected owners or `run_guarded_v3.py`. Thus original inherited hard/soft CORE is an unissued fact; do not assume0 or1. A hard0 environment would also make the new entry's explicit hard>=1 prerequisite fail before science, so original environment admissibility and this view's admission must remain distinct. + +The actual status chain is: + +* A crashing scientific Python child is first waited by **GNU time**. Runner615 puts time outside Python; runner196–211 retains its usage bytes as JSON or raw text. Any selected time diagnostic/format dependence must therefore be checked against time's exact source/image semantics, not inferred from Python's negative returncode. +* Runner890 records its genuine raw wait tuple before decoding at899. Normally that child is **GNU time**, not Python. Time's own normal exit translation may hide a Python core bit from this tuple while still leaving diagnostic bytes or file effects observable. +* A compiler/helper crash is first observed by its own driver/parent. Original compiler stdout/stderr are retained by `cold_build_evidence.py`:181–208,220–282, or by backend/adapters subprocess receipts. GCC13 [collect-utils.cc](https://github.com/gcc-mirror/gcc/blob/releases/gcc-13.2.0/gcc/collect-utils.cc#L94-L120) has a concrete WCOREDUMP-dependent diagnostic. A required helper-fault case could therefore change raw compiler evidence even if a later Python returncode is identical. +* This exact G uses **wait4**, not waitid/si_code. At538–544 it retains `m_status` for **M**, which is outside the science-entry CORE change;630 publishes that raw M status. It is not a demonstrated direct CORE counterexample. Registered child closure and adopted/unreconciled child death have their own failure decisions. If original parents die, adopted science/native descendants require their actual affected status/cleanup map; current G does not publish a per-adopted-child CLD_DUMPED/CLD_KILLED map. + +The declared cold-build control list is `normal, nonzero, timeout, oversized, startup, single_thread, child_limit_and_group, closure_identity`. Source search did not find a deliberately induced SIGSEGV/SIGABRT/SIGILL core fixture in the selected non-evidence scientific/control paths. That bounded negative search is **not a proof that every original fault observation is independent of CORE**. Python exceptions, normal nonzero7, absent exec and SIGKILL retirement do not themselves dump core, so their finite effect slice is not defeated merely by a changed core limit. Fatal native/compiler faults, raw wait observations, raw time/compiler output, possible core-file set and cleanup still need an exact required-case domain argument. + +No actual old/new CORE incompatibility is asserted for the current host, and no G-M-status counterexample is asserted. The precise missing implication is whether every required affected fault has the same original no-dump/status/text/file/cleanup disposition, under genuinely associated original inherited limits and core-dispatch policy. Installed GNU-time source association has not been established here; attempted primary GNU source retrieval was unavailable. This review therefore does **not** invent a GNU-time core-dumped suffix or assert that its format necessarily reads WCOREDUMP. + +## Genuine parent owner versus missing topology and LSM meaning + +The parent authority remains concrete. `environment_association.py`:232–263 authenticates the enrolled private CONTROLLER bundle/callsite;278–301 calls actual `parent_authority.owner_verified`, checks the same controller/registry source and registers this observer before acquisition. It does not issue or replace EX. The original controller acquisition/adoption, complete attempt union, G lifetime, irreversible seal and original retention remain the owning chain. + +Its factual schema61–64 contains source/profile hashes, origins, creation parents, executable files, fixtures, retained root and cases. It reads current credentials and `/proc//attr/current`534–555; public evidence614–615 hashes those actual bytes. Qualification629–704 covers ordinary UID/permission/attribute facts and explicitly leaves installed native effects separate. There is no selected LSM policy rule set, transition/creation result matrix, mountinfo/filesystem/delegation inventory, original binfmt matching vector or original CORE fact in that schema or validator. + +A meaningful **prospective** LSM contract could select, for example, a source-defined kernel configuration with SELinux/AppArmor/BPF/other exec-and-creation mediators disabled and only the individually proved capability/Landlock hooks active; or select exact active policies and derive each required userns, mount/NOSUID, NNP, exec, compiler output, certificate/ACL and error result from their rules. Those are concrete alternatives for a future authorized owning design. **Neither is selected or proved by the inspected profile.** An unconfined-looking subject or policy hash alone cannot establish rule meaning, namespace-entry permission, label inheritance, secureexec, transition denial or raw error ordering. + +For topology, the entry actually owns private propagation/read-only construction and exact writable clones. The missing parent facts are the actual bounded mount graph and relevant filesystem/superblock subjects, absence or proved semantics of automount/remote/FUSE/delegated effects, and interval stability under the original exclusion/no-external-mutation assumptions. A concrete selected ordinary local-filesystem effect slice can support a finite argument; no universal filesystem theorem is required. Current metadata device/inode/mode values do not identify that slice, and no mount-topology/delegation association is implemented here. + +Unchanged metadata criteria remain mandatory: source inventories/modes and real within-invocation custody, inherited/default ACL and native capability consequences, and original outer C2 listxattr refusal over every retained object. Nonempty copied ACLs may not be stripped, mapped to synthetic refusal, or excluded by a new caller-input rule. Neither the fresh binfmt instance nor CORE suppression resolves that separate original consumer obligation. + +## Disposition + +The implemented once-owned private binfmt context is a completed useful source edge and replaces the earlier unexplained old-interpreter absence premise at that narrow scope. Whole caller acceptance remains **HOLD** because the inspected connected source/profile/parent association does not yet derive or own the remaining finite original dispatch, topology/LSM creation/exec and affected fault implications. These are precise source edges, not a request for current live probes, a universal metadata theorem, a new ACL restriction, a new round or budget extension. All current native/kernel/image/policy/object observations, resource fit/funding and executions remain later qualification obligations after an accepted source route exists. diff --git a/research/h25-private-binary-dispatch/review/MOCK-CONTROL-ADMISSION-v1.md b/research/h25-private-binary-dispatch/review/MOCK-CONTROL-ADMISSION-v1.md new file mode 100644 index 0000000000..8002bdd9bb --- /dev/null +++ b/research/h25-private-binary-dispatch/review/MOCK-CONTROL-ADMISSION-v1.md @@ -0,0 +1,9 @@ +Independent source vote22:21:41Z: **GO for a separately parent-admitted bounded compile and execution of the mocked component harness only.** No production native entry, namespace, mount, binfmt context, host registration or kernel probe is admitted. + +Exact harness controls/native-dispatch/dispatch_control.c SHA256c87edf3865eb631c9b7c289fd163572f4a67dbcc286b079d55f92fa5cddc3364; README30e2d873de077fc890efe3d8dd407f0fbafb069f450478f2b35662a0d4f3b9a2; included actual production C60af72915efea62f46ea376e2ba7ed0c616f99b09ca1beec0f4b0d8173609410. All92 harness lines and actual helper/registry path were inspected. Admission must bind all these exact source bytes and the parent's actual compiler/flags/output/clean environment/limits, with no automatic retry or budget extension. This reviewer issues no free-standing compiler command and executes nothing. + +Macros5–8 precede inclusion of the actual native source, redirect syscall and close, and rename production main. Harness main56–92 reaches only create_private_binfmt_dispatch, reserve/acquired/require/failed/close_once and close_all. All their reached native syscall/close boundaries are fixed mocks. Production unshare/mount/prctl/native_start/report/final-exec functions remain uncalled. No constructor, dynamic callback, scientific import, external process launch or namespace setup is present in that reached harness graph. Compilation includes the actual owner rather than a separate model. Ordinary compiler/CRT/library behavior belongs to the parent's bounded component admission, not a native science qualification. + +The fixed mocks inspect the true registry before returning FD7; CREATE checks acquisition/arguments; close checks RETIRED and the actual captured create primary before allowing a secondary errno. Variadic reads match the actual int/unsigned/pointer arguments. Eight reset-isolated case groups cover success plus duplicate call, failed fsopen without CREATE/close, failed CREATE with known close, partial publication plus EINTR/uncertain close, successful publication plus EIO close, namespace-order violation, post-view invocation and registry-capacity exhaustion before syscall. An explicit later close_all checks that uncertain descriptors are not retried. Assertions use explicit check/failure count, not optimization-removable assert. + +A passing result would establish only these actual source-state/error transitions under mocked returns. It cannot establish Linux callback/lifetime behavior, built-in current registration, original exec dispatch, CORE/fault fidelity, topology/LSM effects, full source Q, resource fit, actual caller execution, mathematical improvement or original activity credit. Any change to the included source/harness invalidates this exact source admission until read back. Parent may only run after its own explicit guarded command admission; deadlines and the existing2400-second policy cap are unchanged. diff --git a/research/h25-private-binary-dispatch/review/MOCK-RESULT-ACCEPTANCE-v1.md b/research/h25-private-binary-dispatch/review/MOCK-RESULT-ACCEPTANCE-v1.md new file mode 100644 index 0000000000..79aae7c13f --- /dev/null +++ b/research/h25-private-binary-dispatch/review/MOCK-RESULT-ACCEPTANCE-v1.md @@ -0,0 +1,11 @@ +Independent bounded result disposition: **ACCEPT the guarded compile and eight fixed mocked-owner case groups.** This is not a production native, kernel, handler, complete-caller or original activity PASS. Evidence readback22:30:20Z; no rerun by reviewer. + +Sole admitted guard was validation/run_mock_guard_v3.py SHA25684dd4ee39e68d38bb030b4b40330bd63b2dc52e6bd6e9de18c622885ba643f0e, with admission1d822ab0bb7fbbca3ce42bdf3a47cd0e5ef8315321308d381477bc8643041e64. V1/V2 remained unexecuted. Source/harness stayed exactly60af72915efea62f46ea376e2ba7ed0c616f99b09ca1beec0f4b0d8173609410/c87edf3865eb631c9b7c289fd163572f4a67dbcc286b079d55f92fa5cddc3364. The prebirth checked GCC13 driver was1b99826121ae6682a634e5efe09bd3e3df58ce58e0b28f849114ab5b89139c26. + +execution-mock/FINAL-RESULT.json SHA2568e9ab1892788dd5ecf5808bc0a024c9cf04188dd1c05d05b41cde7b5900340b9 contains two completed actual steps. Compile returned0 in0.3978756439755671 observed seconds, with empty stdout and3252-byte stderr SHA256e7a2833e67848b3d895447c4923b6ff96f22a96ca0a94b49a13f84d3aae1b0c0. The complete raw stderr was read: three pre-existing misleading-indentation warnings at production source189(stat_fd),518(ll_rule) and668(report_failure), with their compiler notes. They are retained evidence; no warning-free claim or source rewrite is made. Compilation made implicit declarations fatal and retained Wall/Wextra diagnostics, as selected before the sole attempt. + +The mocked executable returned0 in0.0022189459996297956 observed seconds, with empty stdout/stderr. All empty capture hashes aree3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855. Neither step reports an error, group kill, timeout or overflow. The eight case groups' only verdict is the explicit C failure-count exit; source review connects those actual checks to the real helper/registry with syscall/close mocks. Passing them establishes those source transitions under the specified returns, not real callback/namespace behavior. The production entrypoint was renamed and uncalled; no scientific module/caller, real fsopen, namespace, mount, handler or host probe ran in this admitted test. + +LEDGER-001.json SHA256dcf67ced06c8223ee3c45237a9872ef72db246bf2c8a24183935e13d3ed8988e records24 conservative seconds charged,1456.890533479/2400 combined,943.109466521 remaining, zero outstanding reservation, no refunds/new budget and physical costs OPEN_NOT_ZERO. Arithmetic is correct. Observed0.40423043695045635 parent wall seconds does not replace the conservative debit or complete cost accounting. All outside preparation/Pro/agents/review/publication/final/historical costs remain OPEN_NOT_ZERO. + +The frozen448-source graph9e5d666cf8a3e59e4e29704438ac5977843583939abf10386f607af2b265c1fa and manifest07824281d6a0dbebacd8430c6e8171e2869871fba50f9a15c33595ad87a2048d were independently hash-checked; all448 actual source modes were0444. All manifest source/profile/control pins matched. The source graph remained unchanged through result readback. Whole S10.03/finite original consequence acceptance is separate and presently withheld; these8groups do not replace52 original helper bodies/65contexts or any required full original case. Original credit delta0 and weighted48% baseline remain unchanged. diff --git a/research/h25-private-binary-dispatch/review/NATIVE-PREREQUISITE-REVIEW-v1.md b/research/h25-private-binary-dispatch/review/NATIVE-PREREQUISITE-REVIEW-v1.md new file mode 100644 index 0000000000..d07955098a --- /dev/null +++ b/research/h25-private-binary-dispatch/review/NATIVE-PREREQUISITE-REVIEW-v1.md @@ -0,0 +1,13 @@ +Independent bounded prerequisite source vote: **ACCEPT the new native dispatch owner conditionally on the explicit selected Linux/configuration interface.** This does not accept the complete caller/Q or grant production-entry/namespace execution. Readback22:19:21Z; original source cutoff22:31:19Z and whole review cutoff22:42:19Z remain fixed. + +Reviewed subject: implementation/native/science_view_entry.c SHA25660af72915efea62f46ea376e2ba7ed0c616f99b09ca1beec0f4b0d8173609410; native/VIEW-ENTRY-CONTRACT-v1.md39c17afc3f0a9981e659c69cc0513be71fb95630f67c5419520e7abc423f1185; bindings/profiles/native.jsond34bf38d322ad3a4331b13aac415ad181c2bb328314d2e8d4e71b6fb975387ef. Complete C delta from the accepted scoped-repair predecessor inspected. + +The actual helper371–397 requires fresh same-ID user/private mount namespaces and private propagation, and rejects post-view/LL/seccomp or duplicate entry. It reserves a registry slot before fsopen, immediately stores the actual positive descriptor through acquired, invokes fsconfig CREATE once, records actual create errno before close, attempts one retirement whether create succeeds or fails, and refuses any uncertain close. Existing reserve/acquired/close_once/failed own the real transitions. fd_count remains bounded96, errors48 and failure report16KiB; this adds one source-owned FD slot without a heap allocation, callback, task or unbounded loop. Main702 invokes the helper after enter_namespaces and before make_view/drop_privilege/Landlock, any descendant or final exec. Final seccomp still contains no fsopen/fsconfig/fsmount/fspick or mount/import allowance. No visible registry mount or host handler change is introduced. + +Source/configuration selection is explicit Linuxv6.12 CONFIG_BINFMT_MISC=y with successful built-in filesystem registration before entry, so lookup does not reach request_module. This is a finite operating-interface premise whose actual image/current association remains later. It is not a compatible boolean or current-host support claim. GNU sched.h declares unshare; build headers must declare the x86-64 syscall numbers. Prefixed UAPI values FSOPEN_CLOEXEC=1 and FSCONFIG_CMD_CREATE=6 agree with the selected primary header. [Linux v6.12 mount UAPI,76/95](https://github.com/torvalds/linux/blob/v6.12/include/uapi/linux/mount.h). + +The kernel lifetime argument was already independently checked against the immutable Linuxv6.12 named functions in the predecessor assessment: publication creates an empty namespace-owned instance; disposing the unattached context does not clear that pointer; namespace destruction ultimately frees it. This accepted source prerequisite supplies the previously absent actual acquisition/create/close edge. It does not itself establish original exec-dispatch equivalence. In particular, ordinary ELF bytes alone do not show that an original miscellaneous handler did not take precedence. + +Contract89 correctly treats vfs_get_tree publication before a later security_sb_kern_mount failure as possible. dispatch_created=false after failed CREATE is not represented as absence of a partial kernel effect. Actual create and close errors remain ordered and paid setup failures before science; cleanup/namespace destruction remains dependent on actual process retirement. These failures cannot become the original optimized refusal. Failure reporting650–670 preserves the new disposition flags and bounded existing raw error/resource state; no success marker or receipt substitutes for external wait/EOF/G ownership. + +No new local source blocker was found in this bounded owner. Complete original-dispatch, CORE/fault, topology, LSM/creation, accounting and genuine parent consequence associations remain separate whole-review obligations. Installed kernel/config/images and actual resource/native results remain later qualification facts; no live host PASS is demanded to author this source. No compiler, mock, native entry, namespace, scientific module or original caller was run by this reviewer. diff --git a/research/h25-private-binary-dispatch/source/VIEW-ENTRY-CONTRACT-v1.md b/research/h25-private-binary-dispatch/source/VIEW-ENTRY-CONTRACT-v1.md new file mode 100644 index 0000000000..7fe304c9b3 --- /dev/null +++ b/research/h25-private-binary-dispatch/source/VIEW-ENTRY-CONTRACT-v1.md @@ -0,0 +1,108 @@ +# Native entry contract for the joined complete caller + +`science_view_entry.c` is an inspectable source implementation for the science branch before GNU time. Its source is not an installed executable, a kernel feature observation, execution permission, an environment compatibility certificate, resource fit, complete S10.03 acceptance or a complete-CLOSE result. Source authoring has performed no compile, import of project code, test, native operation, process/kernel/ACL probe or science execution. + +The original acceptance condition remains assembly and independent review of the **complete assertion-on caller**. The minimum dependency chain is authenticated parent/registry -> exclusive runner layout and relay -> this entry/view -> original GNU time and complete scientific command -> real process/writer retirement and terminal receiver -> original retained-tree seal/archive/restoration/oracles/readback. This entry alone supplies no original activity credit. + +## Fixed native argv and genuine parent inputs + +The runner passes this exact prefix, followed by the original GNU time argv without modification: + +```text +ENTRY + --entry-image ENTRY_PATH ENTRY_SHA256 ENTRY_BYTES + --time-image /usr/bin/time TIME_SHA256 TIME_BYTES + --read-root ORIGINAL_CWD + --artifact-parent ARTIFACT_PARENT ACTUAL_DEVICE ACTUAL_INODE + --science-leaf science + --usage USAGE_PATH ACTUAL_DEVICE ACTUAL_INODE + -- + /usr/bin/time -f ORIGINAL_JSON_FORMAT -o USAGE_PATH -- /usr/bin/python3.12 ... +``` + +`--` is argv index21; the unchanged time argv starts at22. At most160 arguments and65536 total argument bytes are accepted. Each argument is less than PATH_MAX. The exact format is `{"user_seconds":%U,"system_seconds":%S,"kernel_max_rss_kib":%M,"exit":%x}`. The source requires the fixed switches, observer path, Python path, format, `-o` destination and inner delimiter. Complete command/profile/role inverses remain the genuine parent's responsibility; the native prefix is not an alternative command authority. + +`--read-root` is the exact original cwd assertion; its retained name avoids another API change. The selected read/execute scope is `/`, not just that directory. The original cwd, frozen input, reference, loader, library, proc and source paths remain readable. This policy protects writer ownership, source currentness and lifetime containment; it does not claim read confidentiality for unrelated accessible objects. Outer captures may be read but may not be reopened **for writing** or mutated. Prior Pro expressly permits broader tool-tree read/execute when every image inherits the write/lifetime restrictions. This does not make compilers trusted. + +The joined parent authenticates the actual entry source/image association, exact time and original scientific/native images, source/profile/command tuple, and the current objects it creates. Its profile DATA has exactly `schema=joined-native-entry-profile-v1`, `entry_path`, `entry_sha256`, `entry_bytes`, `time_path=/usr/bin/time`, `time_sha256`, `time_bytes`, `source_sha256` and `association_sha256`. The profile hash or the presence of these fields cannot establish that association. Current native memory, loader and installed kernel are external original qualification obligations. + +The runner itself exclusively creates an empty0700 artifact parent and a regular0600 empty single-link usage file, obtains device/inode from actual `fstat`, and retires/closes its creating usage descriptor before launch. The original `science` child remains absent. Captures are siblings outside the artifact parent and remain relay-owned regular0600 files. No capture FD enters this process. `Popen(close_fds=True)` leaves exactly the admitted three descriptors; the entry independently checks a complete bounded descriptor census. No arbitrary approved FD or fourth setup channel exists. + +Entry checks are concrete: role paths are absolute without dot, dot-dot, repeated slash, trailing slash or symlink components; `openat2` uses NO_SYMLINKS|NO_MAGICLINKS. Opened artifact/usage descriptors must match the actual numbers passed, ownership, type, modes, empty directory/file and unique usage link. The entry compares actual getcwd to the admitted original cwd. Path inputs are not evidence of exclusive creation; that fact comes from the real runner's creation/custody chain. + +The entry hashes the complete opened entry image with a16MiB cap, checks stable descriptor metadata, and compares it to `/proc/self/exe`'s actual object. It hashes the opened GNU time image, closes all pre-view FDs, then reopens and rehashes time in the final view and checks the same identity. Both images must be regular, executable, not group/other writable and not setuid/setgid. Existing image link counts are observed and compared for stability; no unnecessary unique-link rule is imposed on a system executable. Each hash preserves stable uid/gid fields within its own namespace. The cross-user-namespace comparison preserves device/inode/mode/link/size/timestamps and full bytes while accounting for translated uid/gid representations: an originally root-owned image is not falsely required to report UID0 inside the one-ID map. Namespace/mount operations do not change kernel inode ownership; original owner qualification remains with the real outer parent. The final path exec requires the original no-external-mutation/exclusive-ownership premise through exec; a supplied hash cannot close a mutable-image race or authenticate already-loaded code. Native-memory/image qualification is still required before execution. + +## Owning setup sequence + +1. Check actual unprivileged equal real/effective/saved UID and GID, empty initial capability sets, genuine proc superblock, exactly one native task and no direct child. The authenticated fresh launch plus the entry's absence of any task-creation call supplies the no-prior-descendant premise. `/dev/null` stdin must be read-only with native major/minor1:3. Stdout/stderr must be distinct blocking FIFO write ends with CLOEXEC clear, not regular captures or sockets. +2. Install and read back the explicit Linux-v6.12 core profile `(1,1)`. An inherited hard CORE below1 refuses before science. This special value suppresses pipe core-handler dispatch in the selected source and is below the file core minimum. Unlike CORE=0, it is not an assumption that pipe handlers obey the ordinary file limit. Final seccomp denies every setrlimit CORE change and every prlimit CORE mutation, including lowering it to0; a NULL-new-limit query remains available. Preservation of required original fault-control observations under this profile is a named Q fact. +3. `unshare(CLONE_NEWUSER)`; write one mapping `uid uid 1`; write `deny` to setgroups before the one mapping `gid gid 1`; check the same numeric IDs. The underlying UID/GID are preserved. Other IDs, supplementary-group representations and named ACL values are not claimed to have unchanged user-namespace representations. No setgroups call clears the original kernel supplementary group list. +4. `unshare(CLONE_NEWNS)` copies the original mount tree and remaps root/cwd. Do not create a PID namespace, pivot_root, chroot, session, process group or child. Set recursive private propagation. Before `make_view`, capability drop, descendants or exec, reserve one FD birth, actually call `fsopen("binfmt_misc",FSOPEN_CLOEXEC)`, immediately acquire the returned descriptor, then call `fsconfig(fd,FSCONFIG_CMD_CREATE,NULL,NULL,0)` once. Record any actual create errno before the one close attempt. Close the context before progressing; any create/close failure stops setup, including uncertain close. No `fsmount`, visible registry mount, host registry change or helper is used. +5. Resolve and validate writable clone sources **in the copied namespace**, then create detached nonrecursive `open_tree(CLONE|CLOEXEC|AT_EMPTY_PATH)` clones of exactly the empty artifact parent and exact usage file. Add NOSUID to each; no preexisting stricter flag is cleared. +6. On the copied root, `mount_setattr(AT_EMPTY_PATH|AT_RECURSIVE)` adds RDONLY|NOSUID. Attach the two detached clones by exact O_PATH/owned directory target handles with move_mount EMPTY_PATH flags. No writable clone of a capture directory, cwd, source tree or administration tree is made. The parent-created science leaf stays absent for the unchanged scientific exclusive mkdir. +7. Re-resolve cwd through the final view, check root, cwd, artifact and usage backing identities, and restore that exact cwd. Reopen the same genuine `/dev/null` through the final read-only view, explicitly retire/once-close the old FD0, then dup the new handle to0 without an implicit close. Read back same backing device/inode/rdev/type/mode, namespace-aware ownership, O_RDONLY, original NONBLOCK status and CLOEXEC clear. A failed old-FD close is uncertain and stops setup; it is never retried or declared complete. Close every other setup/directory/mount FD once. Descendants have not yet been created. +8. Clear ambient and recognized bounding capabilities, lock NOROOT and NO_SETUID_FIXUP securebits, clear effective/permitted/inheritable sets, read back empty sets and install/read back no_new_privs. Install Landlock, close its rule/path/ruleset descriptors, perform another census and authenticate GNU time through the final view. +9. Install the final LP64 seccomp filter and exec the unchanged GNU time command. Successful exec replaces the entry's old executable and mappings and leaves only admitted stdin and two relay writers. Time opens the admitted existing usage inode itself. The original wait owner, PID namespace, genuine proc PID/task data and process-group/session relationships are preserved. + +Unsupported setup fails before GNU time/science. No host configuration is changed, fallback helper is launched or narrower ACL-free scientific-input rule introduced. A negative mount syscall does not become successful setup; attached mounts and namespace flags remain evidence until the parent observes actual process exit. + +## Confinement composition and alias argument + +The final mount tree is recursively private/read-only/NOSUID except the two exact writable clones. VFS mount-write checks, rather than Landlock metadata promises, confine chmod/chown/timestamp/xattr mutations. Required metadata operations remain permitted in the artifact view. The source never strips an ACL, rewrites xattrs, mediates metadata, relocates scientific output out of retention or changes the retention oracle. Existing stricter NOEXEC/NODEV/readonly/etc. flags are never cleared. + +Landlock requires ABI>=6 plus a separately associated compatible successor semantics. It handles all16 ABI6 filesystem rights, both TCP rights, and both signal/abstract Unix scopes. `/` gets READ_FILE|READ_DIR|EXECUTE. The artifact parent additionally gets write/truncate/remove/create regular/dir/symlink/FIFO and REFER. The usage file gets read/write/truncate; no write/create/remove authority is granted over its parent. The exact `/dev/null` object gets read/write for CPython DEVNULL's read/write open and IOCTL_DEV subject to the final filter's six harmless requests, preserving native TCGETS/isatty ENOTTY rather than replacing it with a Landlock denial. No network rule is added. Device/socket creation rights and general device ioctl rights are not granted. + +Every accessible ordinary alias uses the final mount tree and is read-only outside the two admitted clones. Pre-view file/directory/mount/namespace FDs are closed and the trusted entry creates no writable file mapping. This includes replacing stdin's original open file description: even O_RDONLY `/dev/null` on the old writable mount could support a NOW timestamp change through its0666 permissions. The reopened final-view stdin preserves the same backing sink, byte/error behavior and selected status flags. The selected genuine devnull producer has no stdin-OFD identity consumer or shared-position observation; an arbitrary stdin pipe/file/OFD is outside this fixed launch profile. No unknown close is called complete. Prior executable mappings disappear at final exec. Existing aliases of artifact/usage elsewhere in the copied tree are read-only; a hard link cannot acquire writable cross-mount authority, and usage starts with one link. Source image and scientific source single-link/currentness checks remain original checks. + +Proc is genuine and stays in the original PID namespace. A proc root/cwd/exe/FD link to an ancestor-user-namespace task requires the ptrace check. The child user namespace's matching kernel UID does not satisfy commoncap's same-user-namespace cap-subset case, and it has no capability in an ancestor user namespace. Landlock additionally denies ptrace access to tasks outside its inherited domain. Initial namespace map_files access requires initial-user-namespace capability. There is no prior descendant in the new user namespace; all same-new-namespace descendants are born after the complete restrictions and inherit them. Final seccomp prevents setns, unshare, mount APIs and capability/credential recovery. Thus no accessible proc magic link reintroduces a parent's writable root/cwd/FD/executable authority under the selected kernel source premises. The genuine original outer owner can still perform its own required observations from outside. + +The filter kills alternate architectures and x32, returns ENOSYS for clone3, and admits only normalized low32 legacy tuples003d0f00 (thread),00004111 (ordinary spawn) and01200011 (glibc fork). It compares complete tuples, not an allowed-bit union. Linux-v6.12 legacy clone uses lower32 bits. Ordinary fork/vfork have no invented unused-register tests. The selected loader/memory/rseq/robust-list/futex/signal/pipe/wait/exec/thread-teardown vocabulary is explicit in the C array; compatibility of all reached source operations remains image-specific Q, not a guessed availability claim. + +Unlisted syscalls return EPERM. In particular postsetup namespace/mount/fsopen/move_mount/mount_setattr/open_tree, cgroup migration through filesystem writes, socket/socketpair and sendmsg/recvmsg/SCM_RIGHTS, ptrace/process_vm_*, pidfd_getfd, perf/BPF, key/request-key helpers, SystemV/POSIX-message IPC, AIO/io_uring/userfaultfd, handle-based opens and credential/capability changes are unavailable. pidfd_open/send_signal and ordinary signal calls are subject to the inherited Landlock signal scope; prlimit can target only pid0. Fcntl excludes ownership/notification/leases; ioctl permits only the six documented FD-local/observational request values in source. Session/process-group setup remains allowed because it changes neither cgroup membership nor namespace/policy and the original subprocess path requires it. No compiler is exempt from this policy. + +## Failed-resource and terminal map + +Each descriptor acquisition reserves a fixed registry slot before the native syscall. A negative acquisition records FAILED plus actual errno. A successful acquisition records its actual returned integer immediately. Before close, ownership transitions OPEN->RETIRED; there is exactly one close attempt. Success records CLOSED. Any close error records UNCERTAIN and forbids science; numeric FD reuse does not cause a retry. Fixed capacities are96 FD births,48 ordered error objects,4 bounded getdents reads per census,8192 bytes per census buffer and16KiB failure projection. Capacity exhaustion refuses rather than dropping a completion obligation. + +| Resource/effect | Actual owner and failure treatment | Completion evidence | +|---|---|---| +| stdin and two pipe writer endpoints | Runner creates/launches; entry reestablishes the same genuine devnull backing sink through the final view with tracked old0 close/new0 birth; two inherited pipe writers remain in G's domain | Real runner wait, relay EOF and guardian retirement; entry exit alone is insufficient | +| Entry/time image and proc/census descriptors | Entry pending->acquired->once-close; raw syscall errors ordered; no external library finalizer | Source-defined close states plus actual process exit; failure projection is non-success | +| User/mount namespace and mapping writes | Entry creates no descendant; records each completed irreversible stage and every failed syscall | Kernel releases remaining namespace references on actual waited process exit; no self-issued release receipt | +| Detached clones | Entry owns actual open_tree FDs; partial attach remains recorded; all known FD closes attempted once | Actual process exit releases the private mount namespace; no unmount/rollback retry | +| Private binary-format context/instance | Entry reserves/acquires one real fs-context FD, creates once, saves create error before close error and retires/closes once before view; no context retry or mount | Context close releases its superblock; the empty per-user-namespace instance remains until namespace destruction. Failed create may have published before a later LSM failure, so actual process/userns retirement is required and no original optimised-refusal classification is allowed | +| Artifact and usage backing objects | Original runner/registry owns retained paths; entry exposes only exact pinned objects | Original retained-tree registry/oracle/archive custody; entry never unlinks or repairs them | +| Landlock ruleset/path FDs | Entry source-owned; mandatory restriction failure prevents exec; close error is uncertain | Once-close states and external exit; restrictions inherited only after successful restrict_self | +| Final filter/exec | Entry installs mandatory filter and execs exact original observer prefix | Actual outer child launch/wait/streams and complete receiver, never a pre-exit native success marker | +| Loader/setup/close/report/exit tails | Charged to enclosing actual attempt from entry launch; no cost is declared zero | Original enclosing interval/cost ledger; physical fit and final exit tails stay OPEN_NOT_ZERO until guarded evidence | + +On setup or exec failure, the entry writes only a bounded JSON failure object to the relay stderr pipe with schema `h25-native-view-setup-failure-v1`, actual first/ordered secondary errno sites, resource states and completed namespace/mount/restriction flags, then exits78. State numbers are UNUSED0,PENDING1,OPEN2,RETIRED3,CLOSED4,FAILED5,UNCERTAIN6. There is no success message or receipt before final exec. Interrupted native setup, failed stderr projection, lost writer/exit observations or an incomplete JSON projection cannot qualify success. The parent must preserve the raw retained bytes and actual wait disposition, register all pending/native/mount/usage/relay obligations in its existing attempt union and refuse sealing on uncertainty. This file does not create a replacement tracking framework. + +## Outside trust premises and unresolved finite association + +The following are outside this source's self-authentication and must be genuine original parent/profile facts: (a) the fresh trusted runner and this actual loaded native image/loader with a clean initial environment and no prior descendant; (b) selected Linux-v6.12 semantics, or source-reviewed equivalent, and actual namespace/mount/Landlock/seccomp support; (c) a bounded stable mount topology with no unreviewed automount, remote/delegated filesystem or kernel service effect bypass; (d) exact original EX ownership/no external mutation across object validation, mount setup and final pathname exec; (e) original full native/interpreter/compiler/library/source association without trusting compiler behavior; (f) actual G domain placement before birth, owner survival and retirement; (g) selected self resource/fault-control compatibility, including CORE(1,1); (h) enclosing budget/cost coverage for every reached setup, failure and terminal tail. An argv pin, JSON hash or native failure flag establishes none of these facts on its own. + +The former ancestor-binfmt absence premise is replaced by an **implemented entry owner**. Linux-v6.12 `load_binfmt_misc` stops at the first non-NULL per-user-namespace instance; `bm_fill_super` allocates an empty entries list and publishes it in the freshly created user namespace. Actual fsopen/fsconfig CREATE reaches this callback without attaching a visible mount. Its context close invokes `put_fs_context` and deactivates the superblock; `bm_put_super` drops its userns reference but does not erase the namespace's instance. Consequently the empty instance continues shadowing every ancestor F/preopened interpreter until userns destruction. No descendant exists during setup; all descendants inherit the final namespace and policy. Later fsopen/fsconfig/fsmount/fspick, namespace/mount changes and descriptor import are denied. Existing ancestor registry paths stay read-only with no Landlock write rule; the new empty registry has no visible mount at all. + +The selected native interface profile explicitly requires **Linux v6.12 CONFIG_BINFMT_MISC=y and successful built-in filesystem registration**. `get_fs_type` must find that exact already-registered implementation before reaching its request_module branch. This is an operating source/configuration selection whose genuine installed-image association belongs to the parent before execution; neither this document nor profile DATA proves it current. No available/compatible boolean, host probe, module request, host-handler disable or trusted compiler is substituted. Build headers must supply native x86-64 SYS_fsopen/SYS_fsconfig; local prefixed UAPI constants retain v6.12 values FSOPEN_CLOEXEC=1 and FSCONFIG_CMD_CREATE=6 without mixing libc/kernel enum definitions. `` explicitly declares GNU `unshare`. + +Publication is not assumed atomic with the whole fsconfig syscall: vfs_get_tree/bm_fill_super can publish before a later security_sb_kern_mount failure. The entry records `private_dispatch_created=true` only after a successful return; false is **not an assertion that no partial instance exists**. Both a failed create and a failed/uncertain close remain paid setup failures before science, with actual raw errno order and namespace/process teardown obligations. They cannot count as the original optimised refusal. A successful private instance closes the old-reference confinement edge only. The separate finite original-exec/fault/LSM/topology proof must show that shadowing handlers preserves every required GNU-time, Python, compiler/helper, script and failed-exec observation; if an original required case relies on a misc handler, whole acceptance remains blocked. Actual current kernel/image support, numerical fit and execution admission remain later. + +The exact successful-path FD bound is27 lifetime registry births, increased from26 by this one context; cap96 is unchanged, leaving69 slots. The ordered birth groups are startup census/stdin/task/children4; entry/self/time3; ID-map writes3; private context1; artifact/usage/cwd3; detached clones2; copied root/final cwd2; final devnull/dup0 two; Landlock ruleset/root/artifact/usage/devnull5; final census1; final time image1. The context closes before any view handles, so peak stays13 tracked live FDs, or15 actual including the unregistered stdout/stderr writer endpoints. On failure no later acquisition is attempted. At the maximum view/Landlock point at most12 setup FDs require close plus one primary error, so the existing48-error capacity covers that13-error bound; context create+close has at most two errors in original order. FD0/pipe release and all namespace publication/retirement costs remain in the enclosing attempt and are not declared free. + +The joined Q_sigma proof must separately map copied source attributes, default ACL/creation ancestors, executable/library attributes, original negative fixtures and within-invocation custody metadata to actual consumers for every required case. It must preserve the original outer C2 retention listxattr refusal on every retained root/file/directory. Same backing inode or source bytes do not prove that an ACL copied by CPython in the one-ID namespace is unchanged; an omitted consequential ACL can erase an original retention refusal. Nonempty attributes require consequence analysis. Entry contains no synthetic refusal or absence-of-xattrs assertion. Current attributes, current kernel/native images, original negative-case preservation, complete source acceptance and execution GO remain unissued by this native component. + +## Exact inspected predecessor choices and source basis + +Parent coverage freshly pins main7c49fe32c55de02c2c873655c804b16e4c3fef0a; accepted retention PR6736 at0f2a86fa9ef0daf760b77c662a5b762e17848598 is adapted for the bounded once-close/error/SHA mechanics and retained for the genuine outer owner/registry/archive route. PR6744 at1ac58a84b725bd6f17eec66727edc877c49c7e66 supplies unchanged original full bodies/criteria; PR6715 atdc34598c22e2ef43ae7e11fc1d8c14de3915cf89 supplies accepted image adoption. PR6780 ata7903f7ee16c85015f3f9a87fd6f3ae5c9053349 is review-only for the Landlock metadata failure. Parent's bounded overlap coverage reports no direct native copied-view repair; that is not a global novelty claim. The existing `native/retention_guardian.c` is reference and is unchanged by this author. + +Primary source references are source semantics, not current-host observations: + +- [Linux v6.12 mount UAPI](https://github.com/torvalds/linux/blob/v6.12/include/uapi/linux/mount.h) and [namespace implementation](https://github.com/torvalds/linux/blob/v6.12/fs/namespace.c): copy_mnt_ns, open_tree, move_mount and mount_setattr. +- [Linux v6.12 commoncap](https://github.com/torvalds/linux/blob/v6.12/security/commoncap.c): cap_capable and cap_ptrace_access_check; [proc base](https://github.com/torvalds/linux/blob/v6.12/fs/proc/base.c): proc_fd_access_allowed, proc_pid_get_link and map_files access. +- [Linux v6.12 Landlock UAPI](https://github.com/torvalds/linux/blob/v6.12/include/uapi/linux/landlock.h), [syscalls](https://github.com/torvalds/linux/blob/v6.12/security/landlock/syscalls.c) and [task scoping](https://github.com/torvalds/linux/blob/v6.12/security/landlock/task.c). +- [Linux v6.12 clone implementation](https://github.com/torvalds/linux/blob/v6.12/kernel/fork.c): SYSCALL_DEFINE0 fork/vfork and lower_32_bits in legacy clone. +- [Linux v6.12 coredump](https://github.com/torvalds/linux/blob/v6.12/fs/coredump.c): limit==1 aborts pipe dispatch before usermodehelper; file dispatch enforces min_coredump. +- [Linux v6.12 binfmt_misc](https://github.com/torvalds/linux/blob/v6.12/fs/binfmt_misc.c): load_binfmt_misc ancestor lookup, MISC_FMT_OPEN_FILE/file_clone_open and F registration; [exec implementation](https://github.com/torvalds/linux/blob/v6.12/fs/exec.c) retains the actual interpreter execution/custody obligation. +- [Linux v6.12 fsopen/fsconfig](https://github.com/torvalds/linux/blob/v6.12/fs/fsopen.c), [context release](https://github.com/torvalds/linux/blob/v6.12/fs/fs_context.c), [filesystem registration lookup](https://github.com/torvalds/linux/blob/v6.12/fs/filesystems.c), [mount UAPI](https://github.com/torvalds/linux/blob/v6.12/include/uapi/linux/mount.h), and [user namespace destruction](https://github.com/torvalds/linux/blob/v6.12/kernel/user_namespace.c) support the actual private-dispatch construction, exact builtin selection and remaining lifetime edge. +- [Linux v6.12 open metadata](https://github.com/torvalds/linux/blob/v6.12/fs/open.c), [utimes](https://github.com/torvalds/linux/blob/v6.12/fs/utimes.c), [xattr](https://github.com/torvalds/linux/blob/v6.12/fs/xattr.c) and [POSIX ACL](https://github.com/torvalds/linux/blob/v6.12/fs/posix_acl.c): mount-write and namespace representation dependencies. diff --git a/research/h25-private-binary-dispatch/source/native-profile.json b/research/h25-private-binary-dispatch/source/native-profile.json new file mode 100644 index 0000000000..e5aaf56cb9 --- /dev/null +++ b/research/h25-private-binary-dispatch/source/native-profile.json @@ -0,0 +1,24 @@ +{ + "binding": "Detached source manifest pins this exact profile and all exact source leaves; no reverse/future/self hash", + "execution_permission": false, + "required": [ + "Linux cgroup2 preexisting owned suitable delegated subtree; no privilege or host configuration change", + "clone3 CLONE_INTO_CGROUP and CLONE_PIDFD creates M insideD before birth; source-owned waitid P_PIDFD", + "SCM_CREDENTIALS + actual selfpidfd M/K/P/C2; distinct per-role SOCK_SEQPACKET peer and monotone sequence", + "Root/B/Fee no FD198 and no controlwrite; actual parent reports child nativepidfd and own waited/knownclosed state", + "G survives covered role deaths then wholeD retirement; killrequest/frozen/membership/populationempty/waits/FDclose are distinct conjunctions", + "G loss/unknown leaves persistent incomplete and no fresh selected operation/retention success", + "SourceSHA corroborates original external actualentry association; installed nativeimage/current runtime identity not proved by sourcehash", + "Science entry source, actual built image/source association, original time image, namespace/LL ABI6/seccomp semantics and finite observation Q require independently qualified current input; authoring grants no execution", + "Selected native dispatch source profile: Linux v6.12 CONFIG_BINFMT_MISC=y, successful built-in init_misc_binfmt/register_filesystem registration of the exact binfmt_misc implementation before this entry; get_fs_type resolves the already registered built-in without request_module, not a supplied available or compatible flag and not an observed host fact", + "After fresh same-ID user/private mount namespaces, actual entry reserves/acquires one fsopen(binfmt_misc,FSOPEN_CLOEXEC) context, calls fsconfig(FSCONFIG_CMD_CREATE) once, records raw create errno before one close, and closes before view/capability drop/descendants; no fsmount or visible registry mount", + "Linux v6.12 bm_fill_super publishes the fresh user namespace empty binfmt_misc instance; the unattached context may close while that instance shadows ancestor handlers through namespace lifetime; any failed create may have partial publication, remains paid setup failure before science and requires actual namespace/process retirement", + "Final inherited seccomp denies fsopen/fsconfig/fsmount/fspick/mount APIs and descriptor import; no new registry mount is exposed and copied ancestor registry paths have neither mount-write nor Landlock write authority; required original exec/CORE/LSM/topology preservation needs the separate finite consequence proof", + "Native source headers: GNU sched.h supplies unshare declaration; LP64 x86-64 SYS_fsopen/SYS_fsconfig syscall numbers required at build, v6.12 local UAPI FSOPEN_CLOEXEC=1 and FSCONFIG_CMD_CREATE=6; no compiler or native execution implied" + ], + "runtime_admission": "UNISSUED", + "schema": "s03-source-operating-profile-v1", + "selected_design_sha256": "71acd51240bb6c7404bb8e1716de566159a3587d1d342c55f483c749df296407", + "source_only": true, + "subject": "G native lifetime/role/channel source profile" +} diff --git a/research/h25-private-binary-dispatch/source/science_view_entry.c.txt b/research/h25-private-binary-dispatch/source/science_view_entry.c.txt new file mode 100644 index 0000000000..ba28864720 --- /dev/null +++ b/research/h25-private-binary-dispatch/source/science_view_entry.c.txt @@ -0,0 +1,719 @@ +/* Joined complete-caller science entry. SOURCE ONLY; no execution admission. + * + * The authenticated runner execs this image, before GNU time, with precisely + * stdin=/dev/null and two relay pipe writers. This program creates no task. + * It keeps the caller's PID namespace, session, process group and wait owner. + * See VIEW-ENTRY-CONTRACT-v1.md for the fixed argv, external trust premises, + * Linux-v6.12 source basis and the still-unassociated Q_sigma environment. + * No metadata broker, supplied descriptor, compatible flag or ACL stripping. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__x86_64__) || defined(__ILP32__) +#error The entry is the reviewed native LP64 x86-64 ABI only +#endif +_Static_assert(sizeof(void *) == 8 && sizeof(long) == 8, "LP64 required"); +#if !defined(SYS_fsopen) || !defined(SYS_fsconfig) +#error Selected Linux x86-64 build headers must declare fsopen and fsconfig syscall numbers +#endif + +/* Selected v6.12 UAPI shapes, intentionally independent of libc header age. */ +struct sv_mount_attr { uint64_t attr_set, attr_clr, propagation, userns_fd; }; +struct sv_ll_attr { uint64_t fs, net, scope; }; +struct sv_ll_path { uint64_t access; int32_t fd; } __attribute__((packed)); +#define SV_EMPTY_PATH 0x1000U +#define SV_RECURSIVE 0x8000U +#define SV_OPEN_TREE_CLONE 1U +#define SV_MOVE_FROM_EMPTY 4U +#define SV_MOVE_TO_EMPTY 0x40U +#define SV_MOUNT_RDONLY UINT64_C(1) +#define SV_MOUNT_NOSUID UINT64_C(2) +/* Linux-v6.12 include/uapi/linux/mount.h values. Local names avoid requiring + * mutually varying libc/kernel mount-header enum definitions in one TU. */ +#define SV_FSOPEN_CLOEXEC 1U +#define SV_FSCONFIG_CMD_CREATE 6U +#define SV_FS_EXEC (UINT64_C(1)<<0) +#define SV_FS_WRITE (UINT64_C(1)<<1) +#define SV_FS_READ (UINT64_C(1)<<2) +#define SV_FS_READDIR (UINT64_C(1)<<3) +#define SV_FS_RMDIR (UINT64_C(1)<<4) +#define SV_FS_UNLINK (UINT64_C(1)<<5) +#define SV_FS_MKDIR (UINT64_C(1)<<7) +#define SV_FS_MKREG (UINT64_C(1)<<8) +#define SV_FS_MKFIFO (UINT64_C(1)<<10) +#define SV_FS_MKSYM (UINT64_C(1)<<12) +#define SV_FS_REFER (UINT64_C(1)<<13) +#define SV_FS_TRUNC (UINT64_C(1)<<14) +#define SV_FS_IOCTL (UINT64_C(1)<<15) +#define SV_FS_ALL UINT64_C(65535) +#define SV_READ (SV_FS_EXEC|SV_FS_READ|SV_FS_READDIR) +#define SV_ARTIFACT (SV_READ|SV_FS_WRITE|SV_FS_RMDIR|SV_FS_UNLINK|SV_FS_MKDIR|SV_FS_MKREG|SV_FS_MKFIFO|SV_FS_MKSYM|SV_FS_REFER|SV_FS_TRUNC) +#define SV_MAX_FDS 96 +#define SV_MAX_ERRORS 48 +#define SV_MAX_IMAGE UINT64_C(16777216) +#define SV_PROC_MAGIC 0x9fa0 +#define SV_REPORT_CAP 16384 +/* Native x86-64 Linux-v6.12 syscall table. This metadata operation is safe + * under the same final mount-write checks as chmod/fchmod/fchmodat. */ +#define SV_NR_FCHMODAT2 452 + +extern char **environ; +enum sv_fd_state { SV_UNUSED, SV_PENDING, SV_OPEN, SV_RETIRED, SV_CLOSED, SV_FAILED, SV_UNCERTAIN }; +struct sv_fd { int fd; enum sv_fd_state state; const char *site; }; +struct sv_error { const char *site; int number; }; +static struct sv_fd fds[SV_MAX_FDS]; +static struct sv_error errors[SV_MAX_ERRORS]; +static size_t fd_count, error_count; +static bool uncertain, error_overflow, user_entered, mount_entered; +static bool root_private, root_readonly, artifact_attached, usage_attached; +static bool landlocked, filtered; +static bool dispatch_attempted, dispatch_created, dispatch_context_closed; +static uid_t uid; +static gid_t gid; +static int stdin_status_flags; +static const char *artifact_path, *usage_path, *cwd_path; +static struct stat artifact_before, usage_before, cwd_before, root_before, stdin_before; + +static bool failed(const char *site) { + int saved = errno ? errno : EPROTO; + if (error_count < SV_MAX_ERRORS) errors[error_count++] = (struct sv_error){site,saved}; + else { error_overflow = true; uncertain = true; } + errno = saved; + return false; +} +static bool require(bool condition, const char *site) { + if (condition) return true; + errno = EPROTO; + return failed(site); +} +static int reserve(const char *site) { + if (fd_count == SV_MAX_FDS) { errno=EMFILE; failed("fd-registry-capacity"); return -1; } + size_t slot=fd_count++; + fds[slot]=(struct sv_fd){-1,SV_PENDING,site}; + return (int)slot; +} +static int acquired(int slot, long result) { + if (slot < 0) return -1; + if (result < 0) { fds[slot].state=SV_FAILED; failed(fds[slot].site); return -1; } + fds[slot].fd=(int)result; fds[slot].state=SV_OPEN; + return (int)result; +} +static bool close_once(int fd) { + for (size_t n=0;n2) close_once(fds[n].fd); +} +static int opened(const char *path, int flags, const char *site) { + int slot=reserve(site); if(slot<0)return -1; + return acquired(slot,open(path,flags|O_CLOEXEC,0)); +} +/* All role paths must be absolute canonical paths, with no symlink component. + * The one intentional proc magic-link open is separate below. */ +static bool canonical(const char *path) { + if (!path || path[0]!='/' || !path[1] || strlen(path)>=PATH_MAX || path[strlen(path)-1]=='/') return false; + const char *p=path+1; + while (*p) { + const char *end=strchr(p,'/'); size_t n=end?(size_t)(end-p):strlen(p); + if (!n || (n==1 && p[0]=='.') || (n==2 && p[0]=='.' && p[1]=='.')) return false; + if (!end) break; + p=end+1; + } + return true; +} +static bool beneath(const char *path, const char *parent) { + size_t n=strlen(parent); return !strncmp(path,parent,n) && (path[n]==0 || path[n]=='/'); +} +static int role_open(const char *path,int flags,const char *site) { + if(!require(canonical(path),"canonical-role-path"))return -1; + struct open_how how={.flags=(uint64_t)(flags|O_CLOEXEC|O_NOFOLLOW), + .resolve=RESOLVE_NO_SYMLINKS|RESOLVE_NO_MAGICLINKS}; + int slot=reserve(site);if(slot<0)return -1; + return acquired(slot,syscall(SYS_openat2,AT_FDCWD,path,&how,sizeof how)); +} +static bool same_object(const struct stat *a,const struct stat *b) { + return a->st_dev==b->st_dev && a->st_ino==b->st_ino && a->st_mode==b->st_mode && + a->st_uid==b->st_uid && a->st_gid==b->st_gid && a->st_nlink==b->st_nlink && + a->st_size==b->st_size && a->st_mtim.tv_sec==b->st_mtim.tv_sec && + a->st_mtim.tv_nsec==b->st_mtim.tv_nsec && a->st_ctim.tv_sec==b->st_ctim.tv_sec && + a->st_ctim.tv_nsec==b->st_ctim.tv_nsec; +} +/* uid/gid fields are namespace representations, not kernel inode ownership. + * This comparison is used ONLY across the one-ID namespace transition. Both + * image hashes separately retain stable owner fields in their own context; + * dev/ino, all other inventory fields and the complete byte pin still agree. + * Neither unshare nor mount operations change backing inode ownership. Original + * owner/native qualification is retained by the authenticated outer parent. */ +static bool same_backing_across_userns(const struct stat *a,const struct stat *b) { + struct stat represented=*a; + represented.st_uid=b->st_uid;represented.st_gid=b->st_gid; + return same_object(&represented,b); +} +static bool stat_fd(int fd,struct stat *out,const char *site) { + if(fstat(fd,out)<0)return failed(site);return true; +} +static bool decimal(const char *s,uint64_t *value) { + uint64_t n=0; + if(!s||!*s||(s[0]=='0'&&s[1]))return false; + for(;*s;s++){ if(*s<'0'||*s>'9'||n>(UINT64_MAX-(unsigned)(*s-'0'))/10)return false; n=n*10+(unsigned)(*s-'0'); } + *value=n;return true; +} + +/* SHA256 mechanics reused, with predecessor credit, from the accepted + * retention_guardian.c (PR6736 0f2a86fa9ef0daf760b77c662a5b762e17848598). + * The independent native-image/loader association is still an outside premise. */ +struct sha { uint32_t h[8]; uint64_t n; size_t used; unsigned char b[64]; }; +static uint32_t rr(uint32_t x,unsigned n){return (x>>n)|(x<<(32-n));} +static const uint32_t sk[64]={ +0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, +0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, +0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, +0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, +0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, +0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, +0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, +0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2}; +static void sha_block(struct sha*s,const unsigned char*b){uint32_t w[64]; + for(int i=0;i<16;i++)w[i]=(uint32_t)b[i*4]<<24|(uint32_t)b[i*4+1]<<16|(uint32_t)b[i*4+2]<<8|b[i*4+3]; + for(int i=16;i<64;i++){uint32_t x=w[i-15],y=w[i-2];w[i]=(rr(x,7)^rr(x,18)^(x>>3))+w[i-16]+w[i-7]+(rr(y,17)^rr(y,19)^(y>>10));} + uint32_t a=s->h[0],b0=s->h[1],c=s->h[2],d=s->h[3],e=s->h[4],f=s->h[5],g=s->h[6],h=s->h[7]; + for(int i=0;i<64;i++){uint32_t t=h+(rr(e,6)^rr(e,11)^rr(e,25))+((e&f)^(~e&g))+sk[i]+w[i]; + uint32_t t2=(rr(a,2)^rr(a,13)^rr(a,22))+((a&b0)^(a&c)^(b0&c));h=g;g=f;f=e;e=d+t;d=c;c=b0;b0=a;a=t+t2;} + s->h[0]+=a;s->h[1]+=b0;s->h[2]+=c;s->h[3]+=d;s->h[4]+=e;s->h[5]+=f;s->h[6]+=g;s->h[7]+=h; +} +static void sha_init(struct sha*s){*s=(struct sha){{0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19},0,0,{0}};} +static void sha_add(struct sha*s,const void*ptr,size_t n){const unsigned char*p=ptr;s->n+=n; + while(n){size_t a=64-s->used;if(a>n)a=n;memcpy(s->b+s->used,p,a);s->used+=a;p+=a;n-=a;if(s->used==64){sha_block(s,s->b);s->used=0;}}} +static void sha_done(struct sha*s,char out[65]){uint64_t bits=s->n*8;s->b[s->used++]=128; + if(s->used>56){memset(s->b+s->used,0,64-s->used);sha_block(s,s->b);s->used=0;} + memset(s->b+s->used,0,56-s->used);for(int i=0;i<8;i++)s->b[63-i]=(unsigned char)(bits>>(8*i));sha_block(s,s->b); + static const char x[]="0123456789abcdef";for(int i=0;i<32;i++){unsigned v=(s->h[i/4]>>(24-8*(i%4)))&255;out[i*2]=x[v>>4];out[i*2+1]=x[v&15];}out[64]=0;} +static bool hex64(const char*s){if(strlen(s)!=64)return false;for(int i=0;i<64;i++)if(!strchr("0123456789abcdef",s[i]))return false;return true;} + +static bool hash_image(int fd,const char *expected,uint64_t bytes,struct stat *stable) { + if(!require(hex64(expected)&&bytes>0&&bytes<=SV_MAX_IMAGE,"bounded-image-pin"))return false; + struct stat before,after; + if(!stat_fd(fd,&before,"image-before-fstat"))return false; + if(!require(S_ISREG(before.st_mode)&&before.st_size==(off_t)bytes&& + !(before.st_mode&(S_IWGRP|S_IWOTH|S_ISUID|S_ISGID))&&(before.st_mode&0111),"image-object-profile"))return false; + if(lseek(fd,0,SEEK_SET)<0)return failed("image-seek"); + struct sha h;sha_init(&h);unsigned char block[65536];uint64_t count=0; + while(countsizeof block?sizeof block:(size_t)(bytes-count); + ssize_t got=read(fd,block,ask); + if(got<0){if(errno==EINTR)continue;return failed("image-read");} + if(!require(got>0,"image-short-read"))return false; + sha_add(&h,block,(size_t)got);count+=(uint64_t)got; + } + ssize_t end; + do{end=read(fd,block,1);}while(end<0&&errno==EINTR); + if(end<0)return failed("image-end-read"); + if(!stat_fd(fd,&after,"image-after-fstat"))return false; + char actual[65];sha_done(&h,actual); + if(!require(end==0&&same_object(&before,&after)&&!strcmp(actual,expected),"image-stable-bytes-pin"))return false; + *stable=after;return true; +} + +struct sv_dirent { uint64_t ino; int64_t off; unsigned short reclen; unsigned char type; char name[]; }; +/* Bounded source-owned census. No opendir callback or hidden finalizer. */ +static bool enumerate(int fd,int census_kind) { + unsigned char block[8192] __attribute__((aligned(8))); + unsigned count=0;bool seen[3]={false,false,false},done=false; + pid_t self=getpid(); + for(unsigned reads=0;reads<4;reads++) { + long got=syscall(SYS_getdents64,fd,block,sizeof block); + if(got<0)return failed("census-getdents64"); + if(!got){done=true;break;} + size_t at=0; + while(at<(size_t)got) { + if(!require((size_t)got-at>=20,"census-record-header-bound"))return false; + struct sv_dirent *d=(struct sv_dirent *)(void *)(block+at); + if(!require(d->reclen>=20&&at+d->reclen<=(size_t)got&& + memchr(d->name,0,d->reclen-19),"census-record-shape"))return false; + if(strcmp(d->name,".")&&strcmp(d->name,"..")) { + uint64_t n; + if(!require(++count<=SV_MAX_FDS,"census-entry-bound"))return false; + if(census_kind==2) return require(false,"artifact-parent-must-be-empty"); + if(!require(decimal(d->name,&n)&&n<=INT_MAX,"census-native-numeric-name"))return false; + if(census_kind==1) {if(!require(n==(uint64_t)self,"entry-exactly-one-task"))return false;} + else { + bool owned=n<=2||n==(uint64_t)fd; + for(size_t i=0;ireclen; + } + } + if(!require(done,"complete-bounded-census"))return false; + if(census_kind==1)return require(count==1,"one-native-task"); + if(census_kind==2)return require(count==0,"empty-native-artifact-parent"); + return require(seen[0]&&seen[1]&&seen[2],"admitted-stdio-present"); +} +static bool fd_census(void) { + int fd=opened("/proc/self/fd",O_RDONLY|O_DIRECTORY|O_NOFOLLOW,"fd-census"); + if(fd<0)return false; + bool okay=enumerate(fd,0);bool closed=close_once(fd);return okay&&closed; +} +static bool native_start(void) { + uid=geteuid();gid=getegid();uid_t r,e,s;gid_t gr,ge,gs; + if(getresuid(&r,&e,&s)<0||getresgid(&gr,&ge,&gs)<0)return failed("entry-credentials"); + if(!require(uid!=0&&r==uid&&e==uid&&s==uid&&gr==gid&&ge==gid&&gs==gid,"same-unprivileged-real-effective-saved-IDs"))return false; + struct __user_cap_header_struct ch={.version=_LINUX_CAPABILITY_VERSION_3,.pid=0}; + struct __user_cap_data_struct cap[2]={{0},{0}}; + if(syscall(SYS_capget,&ch,cap)<0)return failed("initial-capget"); + if(!require(!(cap[0].effective|cap[0].permitted|cap[0].inheritable|cap[1].effective|cap[1].permitted|cap[1].inheritable),"no-initial-capabilities"))return false; + if(!fd_census())return false; + struct stat input,a,b;struct statfs procfs; + if(fstat(0,&input)<0||fstat(1,&a)<0||fstat(2,&b)<0||statfs("/proc",&procfs)<0)return failed("initial-native-stdio-proc"); + int fl1=fcntl(1,F_GETFL),fl2=fcntl(2,F_GETFL),fl0=fcntl(0,F_GETFL); + if(fl1<0||fl2<0||fl0<0)return failed("initial-stdio-flags"); + if(!require(S_ISCHR(input.st_mode)&&major(input.st_rdev)==1&&minor(input.st_rdev)==3&& + (fl0&O_ACCMODE)==O_RDONLY&&S_ISFIFO(a.st_mode)&&S_ISFIFO(b.st_mode)&& + (a.st_dev!=b.st_dev||a.st_ino!=b.st_ino)&&(fl1&O_ACCMODE)==O_WRONLY&& + (fl2&O_ACCMODE)==O_WRONLY&&!(fl1&O_NONBLOCK)&&!(fl2&O_NONBLOCK)&& + procfs.f_type==SV_PROC_MAGIC,"exact-devnull-and-distinct-blocking-pipe-writers"))return false; + stdin_before=input;stdin_status_flags=fl0; + int stdin_slot=reserve("inherited-devnull-stdin");if(stdin_slot<0)return false; + if(acquired(stdin_slot,0)!=0)return false; + for(int n=0;n<=2;n++) { + int flags=fcntl(n,F_GETFD);if(flags<0)return failed("stdio-fdflags"); + if(!require(!(flags&FD_CLOEXEC),"stdio-survives-final-exec"))return false; + } + int task=opened("/proc/self/task",O_RDONLY|O_DIRECTORY|O_NOFOLLOW,"task-census"); + if(task<0)return false; + bool okay=enumerate(task,1);bool closed=close_once(task); + if(!okay||!closed)return false; + char children[128];int n=snprintf(children,sizeof children,"/proc/self/task/%ld/children",(long)getpid()); + if(!require(n>0&&(size_t)n=1,"core-one-compatible-inherited-hard-limit"))return false; + if(setrlimit(RLIMIT_CORE,&selected)<0)return failed("install-core-one-profile"); + if(getrlimit(RLIMIT_CORE,&actual)<0)return failed("core-one-readback"); + return require(actual.rlim_cur==1&&actual.rlim_max==1,"exact-protected-core-one-profile"); +} +static bool write_mapping(const char *path,const char *bytes,size_t count,const char *site) { + int fd=opened(path,O_WRONLY|O_NOFOLLOW,site);if(fd<0)return false; + /* A proc ID-map write is one operation, never a partial-write retry. */ + ssize_t got=write(fd,bytes,count);bool okay; + if(got<0)okay=failed(site);else okay=require((size_t)got==count,"complete-single-id-map-write"); + bool closed=close_once(fd);return okay&&closed; +} +static bool enter_namespaces(void) { + if(unshare(CLONE_NEWUSER)<0)return failed("enter-same-id-user-namespace"); + user_entered=true; + char map[96];int n=snprintf(map,sizeof map,"%ju %ju 1\n",(uintmax_t)uid,(uintmax_t)uid); + if(!require(n>0&&(size_t)n0&&(size_t)nbinfmt_misc. No fsmount/visible mount is constructed. + * Closing this unattached context drops its superblock, not that namespace + * instance. It continues to shadow every ancestor handler until userns + * destruction; later creation/registration/import is denied by lockdown. + */ + if(!require(user_entered&&mount_entered&&root_private&&!dispatch_attempted&& + !root_readonly&&!landlocked&&!filtered,"once-private-dispatch-before-view-and-lockdown"))return false; + int slot=reserve("private-binfmt-fsopen");if(slot<0)return false; + dispatch_attempted=true; + int context=acquired(slot,syscall(SYS_fsopen,"binfmt_misc",SV_FSOPEN_CLOEXEC)); + if(context<0)return false; + bool created=true; + if(syscall(SYS_fsconfig,context,SV_FSCONFIG_CMD_CREATE,NULL,NULL,0)<0) { + /* Save the actual create errno BEFORE the once-close can replace it. */ + created=failed("private-binfmt-fsconfig-create"); + } else dispatch_created=true; + bool closed=close_once(context); + if(closed)dispatch_context_closed=true; + return created&&closed&&!uncertain; +} +static int clone_mount(int fd,const char *site) { + int slot=reserve(site);if(slot<0)return -1; + return acquired(slot,syscall(SYS_open_tree,fd,"",SV_EMPTY_PATH|SV_OPEN_TREE_CLONE|O_CLOEXEC)); +} +static bool mount_attributes(int fd,uint64_t set,unsigned flags,const char *site) { + struct sv_mount_attr attr={.attr_set=set}; /* Never clear an existing stricter flag. */ + if(syscall(SYS_mount_setattr,fd,"",SV_EMPTY_PATH|flags,&attr,sizeof attr)<0)return failed(site); + return true; +} +static bool attach_mount(int clone,int target,const char *site) { + if(syscall(SYS_move_mount,clone,"",target,"",SV_MOVE_FROM_EMPTY|SV_MOVE_TO_EMPTY)<0)return failed(site); + return true; +} +static bool reestablish_stdin(void) { + /* The old read-only /dev/null FD still carries its OLD writable mount. + * For example a NOW timestamp mutation can use world-write permission, + * even with an O_RDONLY FD. Replace that escape reference through the + * final readonly view; preserve the actual backing sink and stdin bytes. + * Explicit close precedes dup3, so no implicit-close error is discarded. */ + int fresh=role_open("/dev/null",O_RDONLY|O_NONBLOCK,"final-view-devnull-stdin");if(fresh<0)return false; + struct stat actual; + if(!stat_fd(fresh,&actual,"final-view-stdin-fstat"))return false; + if(!require(same_backing_across_userns(&stdin_before,&actual)&& + stdin_before.st_rdev==actual.st_rdev,"same-genuine-devnull-through-final-view"))return false; + int flags=fcntl(fresh,F_GETFL);if(flags<0)return failed("final-view-stdin-flags"); + /* Preserve the admitted sink's original NONBLOCK status, including a + * producer that acquired /dev/null nonblocking. No arbitrary stdin OFD. */ + int selected_flags=(flags&~O_NONBLOCK)|(stdin_status_flags&O_NONBLOCK); + if(fcntl(fresh,F_SETFL,selected_flags)<0)return failed("final-view-stdin-status-flags"); + if(!close_once(0))return false; + int slot=reserve("final-view-stdin-dup");if(slot<0)return false; + if(acquired(slot,syscall(SYS_dup3,fresh,0,0))!=0)return false; + struct stat final;int descriptor_flags=fcntl(0,F_GETFD),status_flags=fcntl(0,F_GETFL); + if(descriptor_flags<0||status_flags<0||fstat(0,&final)<0)return failed("final-stdin-actual-readback"); + if(!require(same_object(&actual,&final)&&final.st_rdev==stdin_before.st_rdev&& + (status_flags&O_ACCMODE)==O_RDONLY&&!(descriptor_flags&FD_CLOEXEC)&& + (status_flags&O_NONBLOCK)==(stdin_status_flags&O_NONBLOCK),"final-stdin-preserved-sink-and-status"))return false; + return close_once(fresh); +} +static bool validate_owned_paths(char **argv,int *artifact,int *usage,int *cwd) { + uint64_t ad,ai,ud,ui; + artifact_path=argv[12];usage_path=argv[18];cwd_path=argv[10]; + if(!require(decimal(argv[13],&ad)&&decimal(argv[14],&ai)&&decimal(argv[19],&ud)&& + decimal(argv[20],&ui)&&ai&&ui,"actual-object-decimal-identities"))return false; + if(!require(canonical(artifact_path)&&canonical(usage_path)&&canonical(cwd_path)&& + !beneath(usage_path,artifact_path)&&!beneath(artifact_path,usage_path)&& + !beneath(cwd_path,artifact_path),"disjoint-exact-native-role-paths"))return false; + if(!require(!strcmp(argv[16],"science"),"fixed-absent-science-leaf-name"))return false; + char actual_cwd[PATH_MAX]; + if(!getcwd(actual_cwd,sizeof actual_cwd))return failed("original-cwd-readback"); + if(!require(!strcmp(actual_cwd,cwd_path),"original-cwd-is-preserved"))return false; + *artifact=role_open(artifact_path,O_RDONLY|O_DIRECTORY|O_NONBLOCK,"owned-artifact-parent");if(*artifact<0)return false; + *usage=role_open(usage_path,O_PATH,"owned-exact-usage-file");if(*usage<0)return false; + *cwd=role_open(cwd_path,O_PATH|O_DIRECTORY,"original-cwd-handle");if(*cwd<0)return false; + if(!stat_fd(*artifact,&artifact_before,"artifact-fstat")||!stat_fd(*usage,&usage_before,"usage-fstat")|| + !stat_fd(*cwd,&cwd_before,"cwd-fstat"))return false; + if(!require(S_ISDIR(artifact_before.st_mode)&&artifact_before.st_uid==uid&& + (artifact_before.st_mode&07777)==0700&&(uint64_t)artifact_before.st_dev==ad&& + (uint64_t)artifact_before.st_ino==ai&&S_ISREG(usage_before.st_mode)&&usage_before.st_uid==uid&& + (usage_before.st_mode&07777)==0600&&usage_before.st_nlink==1&&usage_before.st_size==0&& + (uint64_t)usage_before.st_dev==ud&&(uint64_t)usage_before.st_ino==ui,"genuine-fresh-artifact-and-exclusive-usage"))return false; + if(!enumerate(*artifact,2))return false; + struct stat absent; + if(fstatat(*artifact,"science",&absent,AT_SYMLINK_NOFOLLOW)==0)return require(false,"science-leaf-already-exists"); + if(errno!=ENOENT)return failed("science-leaf-absence-query"); + return true; +} +static bool make_view(char **argv) { + /* Open all writable-clone sources only AFTER copy_mnt_ns has remapped root + * and cwd, so no retained path points into the parent's writable mounts. */ + int art=-1,use=-1,cwd=-1; + if(!validate_owned_paths(argv,&art,&use,&cwd))return false; + int artclone=clone_mount(art,"detached-artifact-mount");if(artclone<0)return false; + int useclone=clone_mount(use,"detached-usage-mount");if(useclone<0)return false; + if(!mount_attributes(artclone,SV_MOUNT_NOSUID,0,"artifact-clone-nosuid")|| + !mount_attributes(useclone,SV_MOUNT_NOSUID,0,"usage-clone-nosuid"))return false; + int root=opened("/",O_PATH|O_DIRECTORY|O_NOFOLLOW,"copied-root-handle");if(root<0)return false; + if(!mount_attributes(root,SV_MOUNT_RDONLY|SV_MOUNT_NOSUID,SV_RECURSIVE,"recursive-readonly-nosuid-copied-tree"))return false; + root_readonly=true; + if(!attach_mount(artclone,art,"attach-exact-writable-artifact"))return false; + artifact_attached=true; + if(!attach_mount(useclone,use,"attach-exact-writable-usage"))return false; + usage_attached=true; + /* Re-resolve through final paths. Do not fchdir a pre-attachment handle. */ + int final_cwd=role_open(cwd_path,O_PATH|O_DIRECTORY,"final-view-cwd");if(final_cwd<0)return false; + struct stat a,u,c,r; + if(fstatat(AT_FDCWD,artifact_path,&a,AT_SYMLINK_NOFOLLOW)<0|| + fstatat(AT_FDCWD,usage_path,&u,AT_SYMLINK_NOFOLLOW)<0|| + fstat(final_cwd,&c)<0||stat("/",&r)<0)return failed("final-view-object-readback"); + if(!require(same_object(&artifact_before,&a)&&same_object(&usage_before,&u)&&same_object(&cwd_before,&c)&& + root_before.st_dev==r.st_dev&&root_before.st_ino==r.st_ino,"same-backing-inodes-root-and-cwd"))return false; + if(fchdir(final_cwd)<0)return failed("reestablish-original-cwd-through-final-view"); + char actual[PATH_MAX];if(!getcwd(actual,sizeof actual))return failed("final-cwd-getcwd"); + return require(!strcmp(actual,cwd_path),"exact-final-cwd-path")&&reestablish_stdin(); +} + +static bool drop_privilege(void) { + if(prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_CLEAR_ALL,0,0,0)<0)return failed("clear-ambient-capabilities"); + /* v6.12 has capabilities 0..40. A later incompatible capability vocabulary + * must be separately associated; this loop also clears recognized <64. */ + for(unsigned n=0;n<64;n++) { + int present=prctl(PR_CAPBSET_READ,n,0,0,0); + if(present<0){if(errno==EINVAL)break;return failed("bounding-set-read");} + if(present&&prctl(PR_CAPBSET_DROP,n,0,0,0)<0)return failed("drop-capability-bounding-set"); + } + unsigned bits=SECBIT_NOROOT|SECBIT_NOROOT_LOCKED|SECBIT_NO_SETUID_FIXUP| + SECBIT_NO_SETUID_FIXUP_LOCKED|SECBIT_KEEP_CAPS_LOCKED; + if(prctl(PR_SET_SECUREBITS,bits,0,0,0)<0)return failed("lock-securebits"); + struct __user_cap_header_struct h={.version=_LINUX_CAPABILITY_VERSION_3,.pid=0}; + struct __user_cap_data_struct zero[2]={{0},{0}},actual[2]={{0},{0}}; + if(syscall(SYS_capset,&h,zero)<0)return failed("drop-all-capability-sets"); + if(syscall(SYS_capget,&h,actual)<0)return failed("final-capability-readback"); + if(!require(!(actual[0].effective|actual[0].permitted|actual[0].inheritable| + actual[1].effective|actual[1].permitted|actual[1].inheritable),"empty-effective-permitted-inheritable"))return false; + if(prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0)<0)return failed("install-no-new-privileges"); + return require(prctl(PR_GET_NO_NEW_PRIVS,0,0,0,0)==1,"no-new-privileges-readback"); +} +static bool ll_rule(int rules,int fd,uint64_t access,const char *site) { + struct sv_ll_path path={.access=access,.fd=fd}; + if(syscall(SYS_landlock_add_rule,rules,1,&path,0)<0)return failed(site);return true; +} +static bool install_landlock(void) { + long abi=syscall(SYS_landlock_create_ruleset,NULL,0,1); + if(abi<0)return failed("required-landlock-ABI-query"); + if(!require(abi>=6,"landlock-ABI-six-or-associated-successor"))return false; + struct sv_ll_attr policy={.fs=SV_FS_ALL,.net=3,.scope=3}; + int slot=reserve("landlock-ruleset");if(slot<0)return false; + int rules=acquired(slot,syscall(SYS_landlock_create_ruleset,&policy,sizeof policy,0));if(rules<0)return false; + int root=opened("/",O_PATH|O_DIRECTORY|O_NOFOLLOW,"landlock-read-root");if(root<0)return false; + int art=role_open(artifact_path,O_PATH|O_DIRECTORY,"landlock-artifact-root");if(art<0)return false; + int use=role_open(usage_path,O_PATH,"landlock-exact-usage");if(use<0)return false; + int null=opened("/dev/null",O_PATH|O_NOFOLLOW,"landlock-devnull");if(null<0)return false; + struct stat sink;if(!stat_fd(null,&sink,"devnull-rule-fstat"))return false; + if(!require(S_ISCHR(sink.st_mode)&&major(sink.st_rdev)==1&&minor(sink.st_rdev)==3,"exact-devnull-rule-object"))return false; + if(!ll_rule(rules,root,SV_READ,"landlock-broad-read-execute")|| + !ll_rule(rules,art,SV_ARTIFACT,"landlock-artifact-write-only-tree")|| + !ll_rule(rules,use,SV_FS_READ|SV_FS_WRITE|SV_FS_TRUNC,"landlock-exact-usage-write")|| + /* IOCTL_DEV is confined to this exact sink and the final filter's six + * harmless requests, preserving isatty/TCGETS's native ENOTTY. */ + !ll_rule(rules,null,SV_FS_READ|SV_FS_WRITE|SV_FS_IOCTL,"landlock-devnull-sink-exception"))return false; + if(syscall(SYS_landlock_restrict_self,rules,0)<0)return failed("landlock-restrict-self"); + landlocked=true;return true; +} + +/* Fixed LP64 policy. All branches inspect native ABI-normalized scalar words; + * no pointer/string inspection, instruction-address authority or bit-union + * approximation for clone. Unlisted syscalls return EPERM. */ +static struct sock_filter program[768];static size_t program_count; +static bool stmt(unsigned short code,uint32_t k) { + if(program_count==sizeof program/sizeof program[0])return require(false,"seccomp-program-capacity"); + program[program_count++]=(struct sock_filter){.code=code,.k=k};return true; +} +static bool jump(unsigned short code,uint32_t k,unsigned char yes,unsigned char no) { + if(program_count==sizeof program/sizeof program[0])return require(false,"seccomp-program-capacity"); + program[program_count++]=(struct sock_filter){.code=code,.jt=yes,.jf=no,.k=k};return true; +} +#define SV_LOAD_NR() stmt(BPF_LD|BPF_W|BPF_ABS,offsetof(struct seccomp_data,nr)) +#define SV_LOAD_ARG(n) stmt(BPF_LD|BPF_W|BPF_ABS,offsetof(struct seccomp_data,args)+(n)*8) +#define SV_LOAD_ARG_HI(n) stmt(BPF_LD|BPF_W|BPF_ABS,offsetof(struct seccomp_data,args)+(n)*8+4) +#define SV_RET(value) stmt(BPF_RET|BPF_K,(value)) +#define SV_EQ(value,yes,no) jump(BPF_JMP|BPF_JEQ|BPF_K,(value),(yes),(no)) +#define SV_ALLOW SECCOMP_RET_ALLOW +#define SV_DENY (SECCOMP_RET_ERRNO|EPERM) +static bool allow_number(int nr) { return SV_EQ((uint32_t)nr,0,1)&&SV_RET(SV_ALLOW); } +static bool install_seccomp(void) { + if(!stmt(BPF_LD|BPF_W|BPF_ABS,offsetof(struct seccomp_data,arch))|| + !SV_EQ(AUDIT_ARCH_X86_64,1,0)||!SV_RET(SECCOMP_RET_KILL_PROCESS)||!SV_LOAD_NR()|| + !jump(BPF_JMP|BPF_JSET|BPF_K,0x40000000U,0,1)||!SV_RET(SECCOMP_RET_KILL_PROCESS))return false; + if(!SV_EQ(SYS_clone3,0,1)||!SV_RET(SECCOMP_RET_ERRNO|ENOSYS))return false; + /* False branch skips this complete clone body (load + 3 tests + 4 RETs). */ + if(!SV_EQ(SYS_clone,0,8)||!SV_LOAD_ARG(0)|| + !SV_EQ(0x003d0f00U,0,1)||!SV_RET(SV_ALLOW)|| + !SV_EQ(0x00004111U,0,1)||!SV_RET(SV_ALLOW)|| + !SV_EQ(0x01200011U,0,1)||!SV_RET(SV_ALLOW)||!SV_RET(SV_DENY)||!SV_LOAD_NR())return false; + /* setrlimit affects self only; CORE is sealed even against lowering to 0. */ + if(!SV_EQ(SYS_setrlimit,0,4)||!SV_LOAD_ARG(0)||!SV_EQ(RLIMIT_CORE,0,1)|| + !SV_RET(SV_DENY)||!SV_RET(SV_ALLOW)||!SV_LOAD_NR())return false; + /* prlimit64 pid_t normalizes to low32. Only pid=0; a CORE query has NULL + * new_limit in both pointer words. Other self resources retain arguments. */ + if(!SV_EQ(SYS_prlimit64,0,12)||!SV_LOAD_ARG(0)||!SV_EQ(0,1,0)||!SV_RET(SV_DENY)|| + !SV_LOAD_ARG(1)||!SV_EQ(RLIMIT_CORE,0,6)||!SV_LOAD_ARG(2)||!SV_EQ(0,1,0)|| + !SV_RET(SV_DENY)||!SV_LOAD_ARG_HI(2)||!SV_EQ(0,1,0)||!SV_RET(SV_DENY)|| + !SV_RET(SV_ALLOW)||!SV_LOAD_NR())return false; + /* fcntl retains ordinary descriptor/lock/pipe commands, never ownership, + * signal delivery, leases or notification. */ + static const int fcntl_commands[]={F_DUPFD,F_GETFD,F_SETFD,F_GETFL,F_SETFL,F_GETLK,F_SETLK,F_SETLKW,F_DUPFD_CLOEXEC,F_GETPIPE_SZ,F_SETPIPE_SZ}; + if(!SV_EQ(SYS_fcntl,0,(unsigned char)(2+2*sizeof fcntl_commands/sizeof fcntl_commands[0]))||!SV_LOAD_ARG(1))return false; + for(size_t i=0;i=sizeof out-used){uncertain=true;goto emit;} used+=(size_t)amount; } while(0) + APPEND("{\"schema\":\"h25-native-view-setup-failure-v1\",\"exit\":78,\"science_started\":false,\"uncertain\":%s,\"error_overflow\":%s,\"user_entered\":%s,\"mount_entered\":%s,\"root_private\":%s,\"root_readonly\":%s,\"artifact_attached\":%s,\"usage_attached\":%s,\"landlocked\":%s,\"filtered\":%s,\"private_dispatch_attempted\":%s,\"private_dispatch_created\":%s,\"private_dispatch_context_closed\":%s,\"errors\":[", + uncertain?"true":"false",error_overflow?"true":"false",user_entered?"true":"false",mount_entered?"true":"false", + root_private?"true":"false",root_readonly?"true":"false",artifact_attached?"true":"false",usage_attached?"true":"false",landlocked?"true":"false",filtered?"true":"false", + dispatch_attempted?"true":"false",dispatch_created?"true":"false",dispatch_context_closed?"true":"false"); + for(size_t i=0;i=29&&argc<=160,"fixed-native-argv-count"))return false; + if(!require(!strcmp(argv[1],"--entry-image")&&!strcmp(argv[5],"--time-image")&& + !strcmp(argv[9],"--read-root")&&!strcmp(argv[11],"--artifact-parent")&& + !strcmp(argv[15],"--science-leaf")&&!strcmp(argv[17],"--usage")&&!strcmp(argv[21],"--"),"fixed-native-argv-switches"))return false; + if(!require(!strcmp(argv[6],"/usr/bin/time")&&!strcmp(argv[22],argv[6])&& + !strcmp(argv[23],"-f")&&!strcmp(argv[24],format)&&!strcmp(argv[25],"-o")&& + !strcmp(argv[26],argv[18])&&!strcmp(argv[27],"--")&& + !strcmp(argv[28],"/usr/bin/python3.12"),"unchanged-original-GNU-time-command-prefix"))return false; + size_t total=0; + for(int i=0;i=0;} + if(okay){self=opened("/proc/self/exe",O_RDONLY|O_NONBLOCK,"actual-self-executable");okay=self>=0;} + if(okay)okay=hash_image(entry,argv[3],entry_bytes,&entry_image)&& + stat_fd(self,&self_image,"actual-self-executable-fstat")&& + require(same_object(&entry_image,&self_image),"actual-native-entry-not-supplied-path-authority"); + if(okay){timed=role_open(argv[6],O_RDONLY|O_NONBLOCK,"actual-time-image-path");okay=timed>=0;} + if(okay)okay=hash_image(timed,argv[7],time_bytes,&time_image); + /* No old-view image/setup FD survives namespace entry. The admitted old + * stdin is explicitly reestablished by make_view; pipes retain only the + * selected relay-writer authority. */ + close_all();if(uncertain||error_count)okay=false; + if(okay)okay=enter_namespaces()&&create_private_binfmt_dispatch()&&make_view(argv)&&drop_privilege()&&install_landlock(); + close_all();if(uncertain||error_count)okay=false; + if(okay)okay=fd_census(); + /* Re-open authenticated time in the FINAL view for a second stable hash; + * close it before path exec, leaving exactly admitted stdio. The remaining + * pathname race is excluded by the named external no-mutation premise, + * never by declaring that a compiler is trusted. */ + if(okay){timed=role_open(argv[6],O_RDONLY|O_NONBLOCK,"final-view-time-image");okay=timed>=0;} + if(okay){struct stat final_time;okay=hash_image(timed,argv[7],time_bytes,&final_time)&& + require(same_backing_across_userns(&time_image,&final_time),"same-authenticated-time-across-userns");} + close_all();if(uncertain||error_count)okay=false; + if(okay)okay=install_seccomp(); + if(okay){execve(argv[6],&argv[22],environ);failed("authenticated-final-GNU-time-exec");} + close_all();report_failure(); + /* Kernel namespace/mount references die with this process, and only an + * actual external wait can establish that final release. No blind retry. */ + _exit(78); +} diff --git a/research/h25-private-binary-dispatch/verify_integrity.py b/research/h25-private-binary-dispatch/verify_integrity.py new file mode 100644 index 0000000000..f3d423d633 --- /dev/null +++ b/research/h25-private-binary-dispatch/verify_integrity.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +"""Passive package membership and SHA-256 integrity only.""" +from pathlib import Path +import hashlib,json +def main(): + root=Path(__file__).resolve().parent + inventory=json.loads((root/'INVENTORY.json').read_text()) + rows=inventory['files'] + expected=set(rows)|{'INVENTORY.json'} + actual={str(p.relative_to(root)) for p in root.rglob('*') if p.is_file()} + if actual!=expected:raise RuntimeError('Package file membership differs') + for name,row in rows.items(): + parts=Path(name).parts + if Path(name).is_absolute() or '..' in parts:raise RuntimeError('Invalid member') + p=root + for part in parts: + p=p/part + if p.is_symlink():raise RuntimeError('Symlink member') + raw=p.read_bytes() + if len(raw)!=row['bytes'] or hashlib.sha256(raw).hexdigest()!=row['sha256']: + raise RuntimeError('Member bytes differ: '+name) + print(json.dumps({'status':'PASS','files':len(rows),'scope':'Passive package bytes only'},sort_keys=True)) +if __name__=='__main__':main() +