Skip to content

luad 0.4.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 18:17
66a8488

luad 0.4.0

Release 0.4.0 built from accepted revision 66a848851caeb7cccd4621b769f94ea09c853242.

Accepted CI run: https://github.com/dweekly/luad/actions/runs/40551057329

  • Bound the parsed model with a chunk-wide instruction budget (#117).
    max_total_instructions (default 2,000,000) is charged when each prototype's code size
    is read, before allocation, in all five dialect parsers. Crossing it fails with
    CORE-LIMIT-004 and exit 5. Machine documents' analysis_configuration.limits gain
    the field. That is a structural addition within schema major 1: a strict consumer
    validating against an older schema snapshot will reject it, so refresh the snapshot
    (luad schema <name>). The pinned public disassembly schema hash is updated
    accordingly.

  • Show a compact summary of each caller's arguments in the luad callers text view
    (string literals quoted, numbers and booleans by value, parameters as param#N,
    other expressions by kind, open windows marked). JSON and JSONL keep the full origin trees.

  • Add an unmodified LuCI 22.03 util.lua beside the 23.05 subset, and tests making the
    release comparison executable. The prototypes without a content-digest match are
    pinned and checked against the textual source diff in debug and stripped builds, a
    one-token edit control is included, and luad's validated layout must predict exactly
    when the official Lua 5.1.5 lister accepts an artifact (stock versus LNUM32).

  • Add callers to the export reference consumer: one-hop callers of a prototype answered
    offline from a verified bundle's records, with the same exactly-once call-site classes
    as luad callers. It joins only on full record context plus the call id. Capped,
    incomplete-family, out-of-inventory and non-5.1 targets refuse. Tests require
    agreement with luad callers on the fixture corpora and the vendored LuCI subset.

  • Add a pinned, unmodified LuCI subset (openwrt/luci openwrt-23.05 at 3d39e8c,
    Apache-2.0) with tests that compile it with official Lua 5.1.5 and check every listed
    same-artifact caller against its source line: the call instruction must sit on a line
    that calls the target by its declared name. Pins a few caller answers with their
    argument evidence and the strict link index's current admission boundary.

  • Add experimental luad callers: the observed one-hop callers of one Lua 5.1 prototype
    across an explicit corpus. It combines accepted same-artifact call relations,
    convention-gated cross-chunk links and verbatim argument origins, with no new
    resolution rule. Every other call site is counted in exactly one class (conflicting,
    degraded, attributed elsewhere, unmapped label, unattributed), so the counts per file
    sum to its call sites. Limit or index uncertainty is marked degraded, so it cannot
    pass for a clean negative. Partial coverage exits 1 and limits exit 5. New
    callers schema; no existing schema changes.

  • Stop the Lua 5.2/5.3 validators from checking SETUPVAL operand B as a source register
    (#108). B is an upvalue index and keeps its upvalue bound; A remains register-checked.
    Official compiler output whose upvalue index exceeds the writing function's stack no
    longer reports false L52-REG-002/L53-REG-002 errors.

  • Refuse unavailable structured disassembly, effects and explanation on Lua 5.2/5.3/5.5 instead of a
    generic projection with zero opcode fields and omitted children. Preserve unverified
    human text listings and their existing all-version scalar-rendering proof. Default/instruction
    export now emits framed per-file failures instead of panicking on those nested inputs.
    Explicit fact selections use an exact profile allowlist and fail before publishing
    partial facts; raw structural inspection/export remains available across all five
    currently read PUC versions. Unknown profiles acquire no availability by default.
    Existing Lua 5.1/5.4 public disassembly and implicit Lua 5.4 export stay available.
    Required official-compiler debug/stripped controls cover selectors, formats, caps,
    mixed/strict completion, raw child facts and reference-consumer failure accounting.
    Retain existing bounded parse diagnostics and limit exit status on refused files;
    explanation selector dispatch cannot bypass profile refusal or report empty success.
    No new decoder, schema version, archive identity or support tier is introduced.

  • Add a bounded export reference consumer with executable/input/schema/stream identities,
    offline schema and completion verification, exact record context, explicit mixed-file
    coverage and truncation, and corruption controls. This experimental example does not
    add a decoder, qualify analysis semantics or promote any target. Public replay also
    reproduced default/instruction export crashes on nested Lua 5.2/5.3/5.5 inputs; the
    reader refuses those attempts and the docs name the structural-selection workaround.
    Replace unsafe SQL
    interpolation guidance with verified, identity-scoped joins and bound parameters.

  • Encode nonfinite machine floats as canonical nan, inf, -inf strings, retaining
    finite JSON numbers, raw source bits and Rust f64 field types. The current schema
    number/string union replaces schema-invalid null values; envelope/JSONL versions
    stay unchanged in the experimental interface. Decoded NaNs carry a class, while
    payload/sign remain in raw evidence. Exact JSON float parsing fixes a reproduced
    one-bit finite round-trip loss. Required source-generated controls cover all five
    PUC dialects, four/eight-byte source floats, tagged model owners and malformed tokens.

  • Reject nonmatching Lua 5.3-5.5 LUAC_NUM header sentinels exactly, including NaNs that
    an epsilon comparison accepted. Official loader/listing controls reject the same
    header mutations; valid header twins pass. No crafted bytecode is executed and no
    target/support tier is promoted.

  • Include emitted parameter_field records in the typed export schema. Actual default,
    selected-origin, mixed-failure and capped CLI streams exercise all sixteen record
    variants; corrupted tags, field access/parameter types and missing context are rejected.
    Record payloads and schema versions are unchanged. Individual record validation does
    not establish stream completion or correct cross-record identity joins.

  • Discard stale Lua 5.1 call-frame facts across callee, argument-origin and capture
    analysis. All CALL result families invalidate the temporary tail before preserving
    explicit results; the combined generic iterator also invalidates its conditional
    control update. Reachable tailcalls cannot preserve overlapping captured cells.
    Reconstructed tables obey the same boundary in definition scans and retained aliases.
    Ordinary live locals, fresh definitions and exact require results remain useful;
    companion/unreachable words do not manufacture capture writes. A clobbered symbolic
    caller no longer supplies a cross-chunk target. Raw/encoded IR facts, machine shapes
    and capability tiers are unchanged. Required official-compiler controls exercise
    the family without executing crafted bytecode.

  • Make explicit luci-module-setglobal links conditional on an admitted straight-line
    root initialization. Final root stores replace stale closure candidates; nested
    function bodies cannot invent exports. Unproved declarations, root calls/table
    mutation, missing/corrupt providers and exhausted admission preserve uncertainty.
    Total retained definitions are capped in canonical artifact order; an incomplete
    index no longer turns a singleton into a unique target. Public required-compiler
    controls cover overwrites, aliases, clobbers, child bodies, input permutations,
    cutoffs and selected JSONL composition. Crafted module-call stack reads and escaped
    selector aliases preserve uncertainty; initialized post-call values remain useful.
    Proof-chain and index-label copying have explicit limits. Linking rejects bytecode
    stdin and documents the immutable-corpus precondition of its two-pass interface.
    This narrows experimental linking and promotes no supported target or runtime
    reachability claim.

  • Fix false diff equality for same-count constant replacements, exact float/string
    representations, capture descriptors, varargs, child changes, header/layout fields,
    trailing bytes and same-length debug changes. Comparison is positional decoded
    content, not behavioral equivalence. --semantic conservatively retains physical
    bits and shows decoded fields/roles without copying constant payloads per use;
    --ignore debug excludes all compared debug names/lines/scopes and rejects typos.
    JSONL now retains chunk-wide differences in the root diff_prototype record.
    Required official Lua 5.1.5 public source-pair tests exercise all formats and modes.

  • gate-argument-origins-lua51 now pins argument-level origin verdicts on the committed
    OpenWrt-derived 32-bit LNUM authority chunk, not only its profile. Structural counts cannot
    catch a precision regression -- the 0.3.0 origins regression left instruction, prototype and
    call-site counts identical while changing answers -- and no non-stock layout had a pinned
    verdict. One of the two pinned verdicts resolves a preserved float literal, which depends on
    decoding numbers under num=8,integral_flag=4, so it fails if the layout handling moves and
    not only if the dataflow does. The pinned set is exact, so a new call site cannot stand in
    for a regressed one, and the chunk is a required_fixtures entry of the gate.

  • luad origins now reports each prototype's parameter field accesses: every constant-key
    table access whose base resolves to one of its own parameters, with the parameter index, the
    key path from the parameter outward, whether it reads or writes, and instruction and
    constant evidence. This answers which keys a function handler(params) reads out of
    params, which previously required reading a flat constant dump and inferring the contract
    from adjacency -- proximity rather than evidence. The base is resolved by the dataflow, not
    by register number, so a reassigned parameter register reports nothing and a key read off
    any other base is excluded. SELF counts as a read. A SETTABLE key is read by operand
    position, since both its key and its value can be constants and taking the first constant
    anywhere in the instruction names the assigned value as the key. The origins schema gains
    parameter_fields on each prototype, and both streaming paths carry the facts as
    parameter_field records: origins --format jsonl and export, whose origin family
    covers them, so a prototype with a contract and no calls is not an empty record set.

  • Stop reporting Lua 5.1 table-literal fields that a callee may have overwritten. The
    reconstruction already detected that a table had been passed to a callee or stored to a
    global or upvalue, but recorded it as incomplete: true while still emitting the fields
    read before that point. incomplete means a field could not be reconstructed, so
    {"dir": "/etc"}, incomplete read as "dir is /etc, other fields may be missing" when
    dir was exactly the value in doubt. Only fields written after the last escape are now
    reported, and the escape itself marks the contents incomplete whether or not a field was
    dropped, because the holder may have added a key this prototype never writes. A table with
    nothing left to report carries no contents. A call that cannot reach the table still changes nothing. This
    is a defect present in 0.3.1, verified against that tag, and independent of the other
    changes here; the previously pinned behaviour is corrected rather than preserved. A consumer
    holding results from 0.3.1 can find exactly which records this moved without re-deriving
    anything: export --format jsonl is deterministic, so build at 8f0890c (the commit before
    this change) and at main, export the same inputs with both, and diff the record_type
    origin lines; each differing record names its call_id. The intervening commits add only
    new parameter_field records and a new arguments array on an open argument window, so an
    origin-only diff over that range isolates this correction. Diff against main rather than
    0b6e8c1: this change shipped with a defect of its own at that commit, corrected in
    27e5e4b.

  • Reconstruct a Lua 5.1 table access whose key is a register, where the key register holds
    only literals. GETTABLE/SELF reported dynamic-key whenever the key was not the
    instruction's own constant operand, without consulting the key register at all, so
    local k = "dir"; sink(params[k]) collapsed the whole argument. One literal key now names
    one field; a key chosen by a branch names one field per key. A key that is not determinable
    -- a call result, a loop variable -- still reports dynamic-key, because a field cannot be
    named without naming its key. A reconstructed key carries no constant id.

  • Report the determinable prefix of an open argument window in Lua 5.1 origin analysis.
    CALL/TAILCALL with a top-dependent argument count emitted only open-argument-window
    and discarded every argument, including the ones at fixed registers below the tail:
    sink("rm -rf " .. params.dir, "flag", helper()) reported nothing at all, losing the one
    argument a reader of a shell-shaped call came for. The window stays open and still reports
    its reason; it now also carries the prefix, dense from index zero with registers contiguous
    from the callee, covering every determinable position rather than only the resolved ones so
    no position is reassigned. The prefix is bounded by the open-result marker and is empty
    without it, because counting registers to the frame size instead would report arguments the
    call does not have. The origins and export schemas gain the field.

  • Resolve the a or b and a and b idioms over locals in Lua 5.1 origin analysis. TESTSET
    had no transfer function, so its write fell through to the unmodeled-write default and the
    tested branch was reported as unknown:overwritten. It is now modeled per edge: the assigned
    value reaches the successor that performs the write, the destination's prior contents reach
    the one that skips it, which is a distinction the control-flow graph already carried as
    conditional-skip. On tests/fixtures/origins_testset.lua, local chosen = primary or secondary moves from alternatives(parameter[1] | unknown:overwritten) to
    alternatives(parameter[0] | parameter[1]), and a chained a or b or c, where one hole
    swallowed two parameters, moves from alternatives(parameter[2] | unknown:overwritten) to
    all three. Folding both outcomes into one state instead would be unsound in the other
    direction, putting the retained value on the edge that assigns; gate-argument-origins-lua51
    pins that it does not.

  • docs/MACHINE-INTERFACE.md now states what overwritten means: a register write the
    analysis does not model, which is why it also covers a register written more than once. It
    was previously readable as a claim that a bounded reaching set had been found and discarded.

  • luad --help and the unrecognized-subcommand error now name the command that answers each
    reader's question, and state plainly that there is no decompiler, pointing at luad inspect
    plus unluac for readable source and at docs/examples/RECIPES.md for worked chains. The
    subcommand list named mechanisms only, so a reader who guessed luad decompile got a verb
    list that could not distinguish an absent capability from a misspelling. The map prints to
    stderr; machine stdout is unchanged.

  • Keep the resolved side of a partly unresolved control-flow join in Lua 5.1 origin
    analysis. A join whose reaching definitions include an Unknown collapsed to
    control-flow-conflict, discarding the definition that did resolve and flattening the
    unresolved one's own reason; it now emits the alternatives set with that definition as
    an option carrying its reason. On tests/fixtures/origins.lua the root-kind histogram
    moves unknown 8 to 7 and alternatives 3 to 4, with no origin losing an expression.
    has_unknown is public so the previous conservative reading stays derivable from a set.

  • Fix stale table contents surviving a write through an alias, in origin analysis. The alias
    check compared the whole slot expression against the whole mutated register, so a table
    nested inside a larger expression was missed, and so was a write through a register
    holding a set of reaching definitions. Both sides are now enumerated: the check recurses
    into subexpressions and matches each table option of the mutated register. The second half
    is a defect present in 0.3.1 and independent of the join-site change, reachable whenever
    two differently shaped bounded sets range over the same table; the join-site change widened
    how easily it is reached. Invalidation stays targeted rather than blanket, so a table that
    is not an alias of the mutated register keeps its expression.

  • control-flow-conflict is now reserved for a loop-carried value widened at
    MAX_BLOCK_REVISITS, where the set of reaching definitions is not finite. It still
    absorbs a join, which termination depends on: widening to a bounded set there was tried
    and reinstated the unbounded lattice growth that 0.3.1 fixed.

Checksums

5bb6e3089a205287f6e0fd43479078287dfbf027bd012e28d74ca9fd552c0bc7  evidence-index.json
3dcf3ce785607c364adec64c45a59195734d0a79197a5c6af9f8d270b9e14450  luad-0.4.0-linux-x86_64.tar.gz
fbdbbb3d3de0b3dfad1a6a7014bb24b6a71fc9e3fd2fa679e8a47ecc4c2dbb30  luad-0.4.0-macos-aarch64.tar.gz
a2dbaec28827fd262b487fe62bc33fbbbb31b6bb2ab5c616412b803cf8afdd61  luad-0.4.0.cdx.json

Build Provenance

Artifact attestations can be verified with:

gh attestation verify luad-0.4.0-<platform>.tar.gz --repo dweekly/luad