Skip to content

gondolin example: shipping a file write as one sh -lc base64 argument fails for files over ~96 KiB (MAX_ARG_STRLEN) #130

Description

@josephkimani

Summary

A gondolin write implementation that ships the whole file as base64 inside a single
sh -lc "...base64 -d..." script embeds that base64 in one execve argument.
Linux caps a single argv string at MAX_ARG_STRLEN (128 KiB), so writing or editing
any file whose base64 exceeds the cap fails at exec time — in practice around
96 KiB of raw content (base64 is ~4/3 the size, plus the surrounding script).
The failure surfaces as a non-obvious exec error, not a "file too large" message.

This affects write implementations built on the shell-and-base64 pattern. The
example as currently checked out writes via vm.fs.writeFile(...) (a library call,
not a shell exec) and is not affected — see "Applicability" below. We hit this
in production on a shell-based write path once a file's base64 crossed the cap.

Observation

The shell-based pattern looks like this:

const b64 = Buffer.from(content, "utf8").toString("base64");
const script = `mkdir -p ${dir}\necho ${b64} | base64 -d > ${file}`;
const r = await vm.exec(["/bin/sh", "-lc", script]); // <-- script is ONE argv string

The entire script string — which contains the full base64 — is passed as a single
argument to /bin/sh. Once b64 pushes the script past 128 KiB, the exec fails.
Observed symptom: the write tool returns something like
write failed (127): ... exec failed. The number is 127/exec failure rather than
a clean error, so it reads like a missing binary rather than an argument-size limit.

Threshold: raw content ~96 KiB (the exact point depends on how much fixed script
text wraps the base64). Files under that write fine, which is why it lurks until a
file grows.

Impact

For a shell-based write path, the write and edit tools silently cap out at ~96 KiB
raw. Any workload that writes or edits larger files (generated code, data files,
long documents) fails at the exec layer with a confusing message, and — because edit
re-writes the whole file — a file that grows past the threshold can no longer be
edited by the tool at all.

Suggested fix

Two options:

  1. Preferred: write through the VM filesystem API rather than a shell round-trip,
    e.g. vm.fs.writeFile(guestPath, content, { encoding: "utf8" }). This has no argv
    limit and is what the checked-out example already does. If the library API is
    available, a shell-based writer should switch to it.

  2. If a shell round-trip is required (older gondolin builds, or a wrapper that
    deliberately stays shell-only), stream the content in chunks instead of one giant
    argument. Append <= 96 KiB base64 chunks to a guest-local staging file with
    printf (a shell builtin, so the chunk is not itself an execve argument —
    only the small enclosing script is), then decode the staging file into place.
    Keep the single-round-trip path for small files. See patch.md.

Applicability

  • The checked-out example (packages/coding-agent/examples/extensions/gondolin,
    index.ts:103-104) writes via vm.fs.writeFile and does not hit this. File
    this only against a copy that uses the sh -lc base64 pattern (e.g. an older or
    derived example), or attach the chunked writer as a hardening for anyone who must
    stay on the shell path.
  • The cap is a property of Linux argv handling, not of gondolin. Whether an older
    gondolin example still ships the shell-based writer we could not verify locally.

Patch reference

patch.md in this directory: a drop-in createGondolinWriteOps that keeps the
single round-trip for small files and switches to chunked printf-append staging
for large ones.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions