Skip to content

Reject null write buffers before opening or truncating files #738

Closed

Description

@LunaStev

Problem

A positive-length null source is detected only by the OS write after the destination has already been opened. fs_write_all therefore destroys existing contents before returning an error; fs_append_all can create a new empty file for the same invalid input. Both currently check only negative lengths before opening: std/fs/file.wave and std/fs/file.wave.

Reproduction

Tested at 5002a93a4a233032553aa6c6e3639d88e93c53bf on Fedora Linux 43 amd64 with LLVM 21.1.8. Build the current compiler with LLVM_SYS_211_PREFIX=/usr/lib64/llvm21 cargo build --locked --release --jobs 2.

The audit used a temporary copy of the repository std, changing only its import prefix to avoid an installed stale std. Recreate it from the repository root:

python3 - <<'PYSETUP'
import pathlib, shutil, tempfile
p = pathlib.Path(tempfile.mkdtemp(prefix="wave-fs-audit-"))
shutil.copytree("std", p / "auditstd")
for f in (p / "auditstd").rglob("*.wave"):
    f.write_text(f.read_text().replace('"std::', '"auditstd::'))
print(p)
PYSETUP

Use the printed path as $audit_dir; save the source below as $audit_dir/repro.wave.

import("auditstd::fs::file")::{fs_write_all};
fun main() {
    println("{}", fs_write_all("target.bin", null, 1, 384));
}
target/release/wavec build "$audit_dir/repro.wave" --dep "auditstd=$audit_dir/auditstd" -O0 --out-dir "$audit_dir/out"
python3 - "$audit_dir/out" <<'PYRUN'
import pathlib, subprocess, sys
p = pathlib.Path(sys.argv[1])
f = p / "target.bin"
f.write_bytes(b"preserve me")
r = subprocess.run([str(p / "repro")], cwd=p, capture_output=True, text=True)
print(r.returncode, r.stdout.strip(), repr(f.read_bytes()))
PYRUN

Repeat with -O2.

Run in a temporary directory containing target.bin with bytes preserve me. At both O0/O2, the program prints -14 and the file becomes empty. Replacing the imported function and call with fs_append_all preserves existing bytes but creates target.bin if it was absent, then prints -14.

A negative-length control prints IO_ERR_INVALID (-4096) and preserves the file. A zero-length write successfully truncates it; keep that valid behavior.

Scope

Reject src == null && len > 0 before opening a path in fs_write_all and fs_append_all, including their portable write/append wrappers. Return the existing invalid-argument result. This requests only the known-null check, not validation of arbitrary pointer accessibility. Keep zero-length semantics. Difficulty: easy.

Completion criteria

  • Positive-length null writes/appends return IO_ERR_INVALID without truncating or creating a file.
  • Negative lengths, valid writes/appends and zero-length calls keep their existing behavior.
  • Extend the native I/O invalid-buffer fixture and verify both existing and missing destinations.

Activity

  1. added
    bugA problem that causes incorrect behavior or crashes.
    good first issueIssues that are good for newcomers or first-time contributors.
    needs testingIssues that require additional testing or verification.
    on Sep 25, 2026
  2. added a commit that references this issue on Sep 25, 2026
    8d66afc
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

    bugA problem that causes incorrect behavior or crashes.good first issueIssues that are good for newcomers or first-time contributors.needs testingIssues that require additional testing or verification.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions