From aef27176bc73f3142111be4ef6868ea2602b1467 Mon Sep 17 00:00:00 2001 From: tebako-ci Date: Sat, 26 Sep 2026 17:58:24 +0800 Subject: [PATCH] spawn plan head: offer the plan only the user arguments, never argv[0] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ruby's rb_exec_fillarg packs the FULL argv into invoke.cmd.argv_buf — first token included. Both array-form call sites (the POSIX hook and the windows hook) handed that buffer to tebako_spawn_runtime_plan verbatim, so the plan's `args` began with the program name. The plan then composed the child argv as [exe, --tebako-image..., --tebako-entry, , "java", "-jar", ...] and the spawned runtime exec'd its entrypoint with the program name as its first USER argument — for the java edge the JVM receives `java` as its main class: Error: Could not find or load main class java Caused by: java.lang.ClassNotFoundException: java Latent since the spec-30 spawn planner landed: every real-world engine spawn was shell form (the shell bridge takes a different path), until ruby-jing 0.1.0 switched to Open3.capture2e — argv form — which is exactly the spawn shape tebako environments want (no /bin/sh). The metanorma 1.17.0 payload carries ruby-jing 0.1.0; its dogfood full-compile legs fail at the RELAX NG validation step on macOS and linux alike (tebako-packages/metanorma nightly run 36230144734; 1.16.9 — ruby-jing 0.0.3, shell form — was green the night before). Fix: at both call sites, advance past argv_buf's first NUL-terminated token before packing `args` (a custom argv0 from the [cmd, name] form is likewise never a user argument). The msys carriers (3.2/3.4/3.4 lines) receive the byte-identical diff payload per the parity spec. Closes the source-factory half of tamatebako/tebako#669. Validated: tools/apply apply-clean with the fix present in process.c for the latest version of every line (3.1.7, 3.2.11, 3.3.12, 3.4.10, 4.0.7) on darwin AND msys; bundle exec rspec 116/0 (incl. the msys byte-parity gate). End-to-end proof lands with the next tebako-runtime-ruby build (the metanorma dogfood's jing leg goes green on a fixed runtime). --- patches/3.1/process_c_tebako_spawn.patch | 38 ++++++++++++++++++- patches/3.2/process_c_tebako_spawn.patch | 38 ++++++++++++++++++- patches/3.2/process_c_tebako_spawn_msys.patch | 38 ++++++++++++++++++- patches/3.3/process_c_tebako_spawn.patch | 38 ++++++++++++++++++- patches/3.3/process_c_tebako_spawn_msys.patch | 38 ++++++++++++++++++- patches/3.4/process_c_tebako_spawn.patch | 38 ++++++++++++++++++- patches/3.4/process_c_tebako_spawn_msys.patch | 38 ++++++++++++++++++- patches/4.0/process_c_tebako_spawn.patch | 38 ++++++++++++++++++- 8 files changed, 296 insertions(+), 8 deletions(-) diff --git a/patches/3.1/process_c_tebako_spawn.patch b/patches/3.1/process_c_tebako_spawn.patch index 5fc9bc9..ebc8a6d 100644 --- a/patches/3.1/process_c_tebako_spawn.patch +++ b/patches/3.1/process_c_tebako_spawn.patch @@ -69,7 +69,7 @@ diff --git a/process.c b/process.c index 97fa336..f55697e 100644 --- a/process.c +++ b/process.c -@@ -2834,6 +2834,818 @@ rb_execarg_get(VALUE execarg_obj) +@@ -2834,6 +2834,854 @@ rb_execarg_get(VALUE execarg_obj) return eargp; } @@ -526,6 +526,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + } @@ -869,6 +887,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + if (tebako_path_is_embedded(prog)) { diff --git a/patches/3.2/process_c_tebako_spawn.patch b/patches/3.2/process_c_tebako_spawn.patch index 5fc9bc9..ebc8a6d 100644 --- a/patches/3.2/process_c_tebako_spawn.patch +++ b/patches/3.2/process_c_tebako_spawn.patch @@ -69,7 +69,7 @@ diff --git a/process.c b/process.c index 97fa336..f55697e 100644 --- a/process.c +++ b/process.c -@@ -2834,6 +2834,818 @@ rb_execarg_get(VALUE execarg_obj) +@@ -2834,6 +2834,854 @@ rb_execarg_get(VALUE execarg_obj) return eargp; } @@ -526,6 +526,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + } @@ -869,6 +887,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + if (tebako_path_is_embedded(prog)) { diff --git a/patches/3.2/process_c_tebako_spawn_msys.patch b/patches/3.2/process_c_tebako_spawn_msys.patch index 39cf38d..34e01ef 100644 --- a/patches/3.2/process_c_tebako_spawn_msys.patch +++ b/patches/3.2/process_c_tebako_spawn_msys.patch @@ -22,7 +22,7 @@ diff --git a/process.c b/process.c index 97fa336..f55697e 100644 --- a/process.c +++ b/process.c -@@ -2834,6 +2834,818 @@ rb_execarg_get(VALUE execarg_obj) +@@ -2834,6 +2834,854 @@ rb_execarg_get(VALUE execarg_obj) return eargp; } @@ -479,6 +479,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + } @@ -822,6 +840,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + if (tebako_path_is_embedded(prog)) { diff --git a/patches/3.3/process_c_tebako_spawn.patch b/patches/3.3/process_c_tebako_spawn.patch index 5fc9bc9..ebc8a6d 100644 --- a/patches/3.3/process_c_tebako_spawn.patch +++ b/patches/3.3/process_c_tebako_spawn.patch @@ -69,7 +69,7 @@ diff --git a/process.c b/process.c index 97fa336..f55697e 100644 --- a/process.c +++ b/process.c -@@ -2834,6 +2834,818 @@ rb_execarg_get(VALUE execarg_obj) +@@ -2834,6 +2834,854 @@ rb_execarg_get(VALUE execarg_obj) return eargp; } @@ -526,6 +526,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + } @@ -869,6 +887,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + if (tebako_path_is_embedded(prog)) { diff --git a/patches/3.3/process_c_tebako_spawn_msys.patch b/patches/3.3/process_c_tebako_spawn_msys.patch index 39cf38d..34e01ef 100644 --- a/patches/3.3/process_c_tebako_spawn_msys.patch +++ b/patches/3.3/process_c_tebako_spawn_msys.patch @@ -22,7 +22,7 @@ diff --git a/process.c b/process.c index 97fa336..f55697e 100644 --- a/process.c +++ b/process.c -@@ -2834,6 +2834,818 @@ rb_execarg_get(VALUE execarg_obj) +@@ -2834,6 +2834,854 @@ rb_execarg_get(VALUE execarg_obj) return eargp; } @@ -479,6 +479,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + } @@ -822,6 +840,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + if (tebako_path_is_embedded(prog)) { diff --git a/patches/3.4/process_c_tebako_spawn.patch b/patches/3.4/process_c_tebako_spawn.patch index 5fc9bc9..ebc8a6d 100644 --- a/patches/3.4/process_c_tebako_spawn.patch +++ b/patches/3.4/process_c_tebako_spawn.patch @@ -69,7 +69,7 @@ diff --git a/process.c b/process.c index 97fa336..f55697e 100644 --- a/process.c +++ b/process.c -@@ -2834,6 +2834,818 @@ rb_execarg_get(VALUE execarg_obj) +@@ -2834,6 +2834,854 @@ rb_execarg_get(VALUE execarg_obj) return eargp; } @@ -526,6 +526,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + } @@ -869,6 +887,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + if (tebako_path_is_embedded(prog)) { diff --git a/patches/3.4/process_c_tebako_spawn_msys.patch b/patches/3.4/process_c_tebako_spawn_msys.patch index 39cf38d..34e01ef 100644 --- a/patches/3.4/process_c_tebako_spawn_msys.patch +++ b/patches/3.4/process_c_tebako_spawn_msys.patch @@ -22,7 +22,7 @@ diff --git a/process.c b/process.c index 97fa336..f55697e 100644 --- a/process.c +++ b/process.c -@@ -2834,6 +2834,818 @@ rb_execarg_get(VALUE execarg_obj) +@@ -2834,6 +2834,854 @@ rb_execarg_get(VALUE execarg_obj) return eargp; } @@ -479,6 +479,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + } @@ -822,6 +840,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + if (tebako_path_is_embedded(prog)) { diff --git a/patches/4.0/process_c_tebako_spawn.patch b/patches/4.0/process_c_tebako_spawn.patch index e6c55b3..578571e 100644 --- a/patches/4.0/process_c_tebako_spawn.patch +++ b/patches/4.0/process_c_tebako_spawn.patch @@ -75,7 +75,7 @@ diff --git a/process.c b/process.c index 97fa336..f55697e 100644 --- a/process.c +++ b/process.c -@@ -2834,6 +2834,822 @@ rb_execarg_get(VALUE execarg_obj) +@@ -2834,6 +2834,858 @@ rb_execarg_get(VALUE execarg_obj) return eargp; } @@ -532,6 +532,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + } @@ -879,6 +897,24 @@ index 97fa336..f55697e 100644 + if (RB_TYPE_P(buf, T_STRING)) { + args = RSTRING_PTR(buf); + args_len = (size_t)RSTRING_LEN(buf); ++ /* argv_buf's first token is the child argv[0] ++ (rb_exec_fillarg packs the full argv); the plan supplies ++ the entry name itself (--tebako-entry), so offer only the ++ user arguments — else the spawned runtime receives the ++ program name as its first argument (tamatebako/tebako#669). ++ A custom argv0 (the [cmd, name] form) is likewise never a ++ user argument. */ ++ if (args_len > 0) { ++ size_t head = strlen(args) + 1; ++ if (head < args_len) { ++ args += head; ++ args_len -= head; ++ } ++ else { ++ args = NULL; ++ args_len = 0; ++ } ++ } + } + if (tfs_spawn_plan_head(eargp, prog, args, args_len)) return; + if (tebako_path_is_embedded(prog)) {