summaryrefslogtreecommitdiff
path: root/internal/namespace/namespace.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/namespace/namespace.go')
-rw-r--r--internal/namespace/namespace.go41
1 files changed, 29 insertions, 12 deletions
diff --git a/internal/namespace/namespace.go b/internal/namespace/namespace.go
index 1a09b78..b0794a4 100644
--- a/internal/namespace/namespace.go
+++ b/internal/namespace/namespace.go
@@ -2,7 +2,6 @@ package namespace
import (
_ "embed"
- "encoding/json"
"fmt"
"os"
"os/exec"
@@ -55,15 +54,13 @@ func VerifyIsolation() (bool, string) {
return true, "Isolated and root"
}
-// VerifyArguments prints the current process arguments as a JSON array.
-// This is used for E2E testing to verify that argument splitting and
-// shell injection are not occurring during the bootstrap loop.
+// VerifyArguments prints the current process arguments as hex-encoded strings.
+// This is used for E2E testing to verify that the data path is 8-bit clean
+// and that no bytes are mutated during the bootstrap loop.
func VerifyArguments(args []string) error {
- out, err := json.Marshal(args)
- if err != nil {
- return fmt.Errorf("failed to marshal arguments: %w", err)
+ for i, arg := range args {
+ fmt.Printf("%d:%x\n", i, arg)
}
- fmt.Println(string(out))
return nil
}
@@ -74,6 +71,16 @@ func Bootstrap() error {
return nil
}
+ // 0. Validate current arguments for null bytes before proceeding.
+ // If any argument contains a null byte, syscall.Exec will fail with 'invalid argument'.
+ for i, arg := range os.Args {
+ for j := 0; j < len(arg); j++ {
+ if arg[j] == 0 {
+ return fmt.Errorf("argument %d contains null byte at position %d", i, j)
+ }
+ }
+ }
+
self, err := os.Executable()
if err != nil {
return fmt.Errorf("failed to get executable path: %w", err)
@@ -89,16 +96,16 @@ func Bootstrap() error {
// 2. Write the embedded launcher binary to the temp file.
if _, err := tmpFile.Write(launcherBytes); err != nil {
- tmpFile.Close()
+ _ = tmpFile.Close()
return fmt.Errorf("failed to write launcher binary: %w", err)
}
-
+
// Ensure the binary is executable (0700)
if err := tmpFile.Chmod(0700); err != nil {
- tmpFile.Close()
+ _ = tmpFile.Close()
return fmt.Errorf("failed to set launcher permissions: %w", err)
}
- tmpFile.Close()
+ _ = tmpFile.Close()
// 3. Prepare arguments for the launcher.
// The launcher expects: launcher <command_to_run> [args...]
@@ -106,6 +113,16 @@ func Bootstrap() error {
args = append(args, os.Args[1:]...)
// 4. Replace the current process with the launcher.
+ // We must check for null bytes in the arguments here because syscall.Exec
+ // (which calls execve) will return 'invalid argument' (EINVAL) if any
+ // string in the argv array contains a null byte.
+ for i, arg := range args {
+ for j := 0; j < len(arg); j++ {
+ if arg[j] == 0 {
+ return fmt.Errorf("launcher argument %d contains null byte at position %d", i, j)
+ }
+ }
+ }
err = syscall.Exec(launcherPath, args, os.Environ())
if err != nil {
return fmt.Errorf("launcher exec failed: %w", err)