TYCHO Search

Search documentation

    ↑ ↓ to move · Enter to open · Esc to close

    Brief for an external security review

    Status: maintained packet for a third-party reviewer (ROADMAP §7, still open). Scope, threat model, prior passes, and reproducing lanes below; the FFI review and SECURITY.md are pointed at, not summarised.

    ROADMAP.md §7 asks for a third-party audit and has been open since the roadmap was written. It is the last 1.0 condition that no amount of internal work can close, and it stayed open partly because "get an audit" is not a task anyone can pick up — it needs a scope, a threat model, and an account of what has already been looked at. This is that packet. Hand it to a reviewer.

    It deliberately does not summarise docs/internals/ffi-review-2026-08-14.md or SECURITY.md; it points at them and adds what a stranger needs that an insider would not think to write down.


    1. What the thing is, in one paragraph

    Tycho is a compiled language whose self-hosted compiler (compiler/, about 26k lines of Tycho) transpiles to C and hands the result to cc; bootstrap/tycho_compiler.c is the generated cold-start artifact, not a second implementation. There is no VM and no runtime interpreter. Programs get value semantics, arena allocation freed at scope exit, and an affine handle type whose destructor runs at scope exit. The standard library (corelib/, 89 packages) is written in Tycho except where it binds a system library, and those bindings are the subject below.

    The security-relevant consequence of the design: a Tycho program's memory safety is whatever the emitted C has, plus whatever the hand-written C shims have. The first is machine-generated and sanitizer-tested; the second is hand-written and is where a reviewer should spend their time.


    2. Threat model — where untrusted bytes actually enter

    Ranked by how directly attacker-controlled data reaches hand-written C.

    entry pointpackagereaches
    an HTTP response bodycore:http (libcurl)a realloc accumulation loop in http_shim.c
    a TLS streamcore:tls (OpenSSL)tls_shim.c, 128 lines, client side only
    a decompressed streamcore:compress (zlib)an output-growth loop; reached from tycho-ar, core:zip, any gzip body
    an archivecore:zip, tools/tycho-arle16/le32 offset arithmetic, path handling (zip-slip)
    a PNGcore:image (libpng)size arithmetic before a malloc
    an HTTP requestcore:httpd, server/request-line and header parsing, connection lifetime
    a text documentcore:json, csv, toml, markdown, utf8pure Tycho, but feeds everything above
    a key or ciphertextcore:crypto (OpenSSL)key import, AEAD, memory hygiene
    a SQL parametercore:sqliteprepared statements, sqlite3_bind_text

    Two things a reviewer should know before reading any of it:


    3. What has already been measured, and the scope of each claim

    This is the part that saves a reviewer time, and every row understates rather than overstates. Each named lane is runnable — see §5.

    claimhow it is heldwhat it does NOT cover
    core:tls verifies chain AND hostnamemake tls-verify — a real openssl s_server on loopback, three outcomes that must disagreethe tls shim's memory handling, error paths, session lifetime
    core:http verifies certificatesmake http-verifyanything above the transport; the CA-override env vars are trusted input
    no secret survives in memory the crypto shim releasedsh scripts/crypto_hygiene.sh — interposes free, scans released blocksmemory the shim never owned (OpenSSL's own allocations, the runtime's FFI copy)
    key-import hex decode is constant-timesame lane, under valgrind with the input marked undefinedcache-line and table-lookup side channels; everything else in the package
    decompression cannot run awaya 1 GiB ceiling with its own error, proved to fire at a reduced ceilingnothing else in zlib's surface
    the emitted C traps out-of-bounds indexingprobes with the true length in the messagebytes/string slices CLAMP by design (FRICTION #5)
    Windows undefined behaviourmake wine-ubsan — 361 fixtures, trap-mode UBSanuse-after-free and heap overflow: mingw-w64 here ships no ASan
    text parsers survive mutationscripts/fuzz_shims.shseed quality is the whole game — see §4
    markdown escapes text, scheme AND attributescorelib/test/markdown, with the escaper defeated as a controlraw-HTML passthrough is not a feature, so it is untested rather than proven absent

    Nothing here is a substitute for §7 and this table is not a defence. It is a map of which stones have been turned over by someone who chose which stones to turn.


    4. Why an outside reviewer is expected to find things

    Not an argument from principle — from the record.

    Three passes, three different sets of targets, zero overlap between them. That is the shape of a surface that has not been reviewed enough, and the reason §7 is written as a hard condition rather than a nice-to-have.

    The other half of the record is about instruments, and it is the more useful warning. Roughly a third of the time in those passes went into measurements that were themselves wrong *and looked like results*: a fuzzing corpus whose seeds died at the first byte while reporting 1950 clean mutants; a wine sweep where every invocation was command not found; a differential lane that scored 1197 clean answers while the defect it was built for sat untested in front of it, because the corpus was integers and the bug was in floats. Budget for the instrument being the defect. Every lane added since refuses to score anything until a deliberately wrong expectation has been shown to fail.


    4b. There is a released artifact now

    0.9.2 shipped 2026-11-12 with prebuilt Linux, Windows and macOS tarballs, so a reviewer does not have to build anything to start:

    gh release download v0.9.2 -R StefanVonRanda/tycho
    sha256sum -c tycho-v0.9.2-linux-x86_64.tar.gz.sha256
    shasum -a 256 -c tycho-v0.9.2-darwin-arm64.tar.gz.sha256
    

    The source at tag v0.9.2 is what those binaries were built from. Building from source is still the better basis for a review — the tarball tells you nothing about how it was made — but the artifact is there if you want to reproduce a finding against exactly what users get.

    5. Reproducing anything here

    No cloud CI, deliberately. Everything runs locally with a C compiler and make.

    make                      # build ./tychoc from the self-hosted compiler
    make test                 # 1039 fixtures under ASan/UBSan/LeakSanitizer
    make corelib              # every corelib package against its golden
    make ci                   # the whole sweep, four lanes in parallel
    

    Timings are given as ranges, and the ranges are wide on purpose. Measured across four green sweeps on one machine in one afternoon (2026-09-18): the whole of make ci between 422s and 1123s, make test between 82s and 144s, make corelib between 169s and 379s. Steps that were not touched between runs moved by 2x, so a single number here would be an invented precision — see FRICTION 91 for the measurement and why no figure in this tree should be read off one run. Expect single-digit minutes on a workstation; budget more on a shared or throttled box.

    make ci runs under gcc or clang (both measured green, 2026-09-18). It needed gcc specifically until then, for three unrelated reasons that were all in the gates rather than the language — FRICTION 89, 90 and 92, each with the compiler measurement that closed it.

    Security-specific lanes, each cheap and each with its controls documented in its own header:

    make tls-verify           make http-verify          make math-diff
    sh scripts/crypto_hygiene.sh                        sh scripts/fuzz_shims.sh
    make wine-ubsan           # ~13 min, Windows UB only
    sh scripts/asan_self.sh   # the COMPILER under ASan/UBSan over the whole corpus
    

    CONTRIBUTING.md has a table mapping each part of the tree to the one lane that can redden for it. A package whose system dependency is absent is skipped, not failed, and the verdict line names it — read the last line, not the exit code.


    6. Where we would start, if we were you

    Offered as a starting point, not a scope — a reviewer who picks differently is the entire point of the exercise.

    1. corelib/zip/ and tools/tycho-ar. Archive parsing is offset arithmetic over attacker-controlled integers, and the zip-slip defence rests on path.safe_join, which has a property test but no adversarial one. The fuzzing seeds for this were wrong twice before they reached the parser at all.
    2. core:httpd and server/. Slowloris is bounded, not eliminated, and the residual is a design property of connection-per-worker. Request parsing survived every malformed shape thrown at it by one person.
    3. The FFI signature surface itself. Not any single shim — the fact that nothing checks an extern fn against the C it names. A systematic diff of declarations against headers is work nobody has done. Start with the interior-NUL class, and take the history as the warning: the rule is normative in docs/spec/14-ffi.md, a deliberate sweep for it ran on 2026-08-13, three packages carry explicit guards naming each other — and two packages that were never on that list both had the bug, one of them collapsing two passwords into one derived key (FRICTION #70, #75, #76, #77). Mechanically listing all 46 externs that take a string found them in a single pass. Grep the declarations; do not reason about which ones matter.
    4. core:crypto beyond the comparison. cx_ct_equal uses CRYPTO_memcmp and the key-import decode is constant-time; the rest of the package's side-channel surface is unexamined.
    5. Windows use-after-free. Structurally uncovered: mingw-w64 here ships no ASan, so make wine-ubsan catches undefined behaviour and nothing else.

    7. What we want back

    A list of findings with reproductions, and — more valuable — which classes you looked for and found nothing in, since that is what tells us where the map is genuinely blank rather than merely unvisited. Negative results with their controls are recorded here as first-class (see the markdown entry at the end of FRICTION.md), and yours would be too.

    Report privately per SECURITY.md.