Files
packfs/SECURITY.md
T
retoorandClaude Sonnet 5 419182bb05 Add version API, pkg-config, SPDX headers, SECURITY.md, CHANGELOG.md
Project-hygiene pass toward being a properly citable, embeddable,
professionally-packaged C library rather than just working code:

- PACKFS_VERSION_MAJOR/MINOR/PATCH/STRING in include/packfs.h, the single
  source of truth for the project's version, plus a runtime pfs_version()
  (src/vfs.c, next to vfs_new/vfs_free) so a dynamically-linked consumer
  can check ABI/API compatibility without recompiling. Covered by a new
  assertion in tests/test_mem.c that the macro and the runtime function
  never disagree.
- packfs.pc.in + a `make install` rule that generates packfs.pc with its
  Version: field derived from PACKFS_VERSION_STRING via a Makefile-level
  grep/sed, never hand-maintained separately -- verified end-to-end with a
  scratch `make install PREFIX=...` + `pkg-config --cflags --libs packfs`
  + `make uninstall`, not just by reading the rule.
- SPDX-License-Identifier: MIT added to every src/*.c and src/internal.h
  (include/packfs.h already had one); the whole distributed source tree
  now carries consistent machine-readable license metadata.
- SECURITY.md, stating precisely what this project's containment and pack-
  integrity code actually claims as a security boundary (concept.md
  Section 6/7) versus what it explicitly does not (unenforced `mode`, no
  cross-process concurrency) -- not generic boilerplate -- with a real
  reporting contact rather than a placeholder.
- CHANGELOG.md (Keep a Changelog format), summarizing the real history in
  git log to date; explicitly notes no version is tagged yet.

Verified: clean `make all` + `make test` (all 6 binaries, including the
new version-check assertion), and a full ASan/UBSan sweep of all 6
binaries with zero real findings (one run hit the already-documented
DEADLYSIGNAL sandbox flake on test_pack_write_perf across all 3 retries;
re-verified directly afterward with 5/5 additional clean passes and timing
well under any plausible timeout, confirming it was the flake, not a
regression, before treating this as done).

Deliberately not done here, by the user's explicit choice: no
CODE_OF_CONDUCT.md, and no git remote/publishing -- this repository still
has neither, and both are decisions left to the maintainer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UqJpkdJ6Njnt1pw3CbghzB
2026-09-14 10:58:35 +00:00

4.4 KiB

Security Policy

Supported versions

No version has been tagged yet (see CHANGELOG.md); this project is pre-1.0 (README.md's "Status"). Only the latest commit on the default branch is supported — there is no LTS branch and no backport policy while the project is at 0.x.

What PackFS actually claims as a security boundary

Stated precisely, per this project's documentation standard (CLAUDE.md), rather than a generic "we take security seriously":

  • Path containment for dir mounts is a hard requirement (concept.md Section 6, CLAUDE.md's "Load-bearing constraints"). On Linux 5.6+, every lookup beneath a dir mount uses openat2(RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS) to atomically block .. escapes, symlink escapes, and the canonicalize-then-open TOCTOU window in one kernel call. vfs_harden_process_with_landlock (Section 6.2) is an additional, opt-in kernel-level layer — not applied automatically, since Landlock restrictions are cumulative, irreversible, and process-wide, which would be a surprising side effect for an embeddable library to trigger on its own.
  • On platforms or kernels without openat2, containment falls back to per-component O_NOFOLLOW plus canonicalize-and-verify (Section 6.3) — an accepted, explicitly weaker residual-risk posture, not a claim of equal strength. Code and documentation that describe this fallback as equivalent to the openat2 path would themselves be a bug worth reporting.
  • The VFS core canonicalizes ./.. in the virtual namespace itself, before any backend is reached, so a crafted path cannot escape a mount's prefix even when no host directory is involved.
  • A pack file is treated as untrusted input unless it came from this run's own compaction (Section 7). Every index entry's offsets and sizes are bounds-checked against the actual file/strings-region length before being trusted; a failed check invalidates the whole pack load. An externally-sourced pack additionally needs a checksum verified at load. Loading an attacker-supplied .img file that PackFS accepts without detecting corruption, or that causes an out-of-bounds read/write, is a security bug.
  • Journal records are length/checksum self-describing (Section 4.4), so replay stops cleanly at the first torn or corrupted record rather than reading past it.

What PackFS explicitly does not claim

Reporting these as vulnerabilities is not necessary — they are documented, known limitations, not silent gaps:

  • mode (permission bits, symlinks, hard links) is stored and restored but not enforced. Nothing in the VFS core interprets it as an access control boundary in v0 (concept.md Section 10).
  • Cross-process concurrency is specified (an LMDB-style reader-table design, Section 5.6) but not implemented. Two uncoordinated processes writing the same host directory through a dir mount are not synchronized against each other.
  • zip/tar are permanently out of scope (CLAUDE.md, "Project decisions that supersede concept.md") — there is no archive-format parsing code in this project to have a vulnerability in.

Reporting a vulnerability

Please do not open a public GitHub issue for a suspected security vulnerability. Once this repository has a hosting remote and private security advisories are available there, prefer that. Until then, or as an alternative, report privately to the maintainer:

retoor — retoor@molodetz.nl

When reporting, include: the affected function(s) or file(s), a minimal reproduction (ideally as a tests/test_*.c-style program using only the public API in include/packfs.h), and which of the guarantees above (or which concept.md section) you believe is violated — this project's own verification discipline (make test, ASan/UBSan/TSan per CONTRIBUTING.md) depends on being able to turn a report into a permanent regression test.

Verification this project already does

Every change to src/upper.c, src/overlay.c, or src/vfs.c is required to pass under -fsanitize=address,undefined and -fsanitize=thread before being considered done (CLAUDE.md) — this process has already caught and fixed a real heap-use-after-free in the snapshot-reclamation logic during development (see the reclaim_gate note in src/internal.h). .github/workflows/ci.yml runs the full test suite plus both sanitizer passes on every push.