Repository navigation
luad 0.4.0
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-004and exit 5. Machine documents'analysis_configuration.limitsgain
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 callerstext view
(string literals quoted, numbers and booleans by value, parameters asparam#N,
other expressions by kind, open windows marked). JSON and JSONL keep the full origin trees. -
Add an unmodified LuCI 22.03
util.luabeside 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
callersto 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
asluad 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 withluad callerson the fixture corpora and the vendored LuCI subset. -
Add a pinned, unmodified LuCI subset (openwrt/luci
openwrt-23.05at 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 markeddegraded, so it cannot
pass for a clean negative. Partial coverage exits 1 and limits exit 5. New
callersschema; no existing schema changes. -
Stop the Lua 5.2/5.3 validators from checking
SETUPVALoperand 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 falseL52-REG-002/L53-REG-002errors. -
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,-infstrings, 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_fieldrecords 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-setgloballinks 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
diffequality 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.--semanticconservatively retains physical
bits and shows decoded fields/roles without copying constant payloads per use;
--ignore debugexcludes all compared debug names/lines/scopes and rejects typos.
JSONL now retains chunk-wide differences in the rootdiff_prototyperecord.
Required official Lua 5.1.5 public source-pair tests exercise all formats and modes. -
gate-argument-origins-lua51now 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 undernum=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 arequired_fixturesentry of the gate. -
luad originsnow 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 afunction 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.SELFcounts as a read. ASETTABLEkey 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. Theoriginsschema gains
parameter_fieldson each prototype, and both streaming paths carry the facts as
parameter_fieldrecords:origins --format jsonlandexport, whoseoriginfamily
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 asincomplete: truewhile still emitting the fields
read before that point.incompletemeans a field could not be reconstructed, so
{"dir": "/etc"}, incompleteread as "diris/etc, other fields may be missing" when
dirwas exactly the value in doubt. Only fields written after the last escape are now
reported, and the escape itself marks the contentsincompletewhether 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 jsonlis deterministic, so build at8f0890c(the commit before
this change) and atmain, export the same inputs with both, and diff therecord_type
originlines; each differing record names itscall_id. The intervening commits add only
newparameter_fieldrecords and a newargumentsarray on anopenargument window, so an
origin-only diff over that range isolates this correction. Diff againstmainrather 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/SELFreporteddynamic-keywhenever 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 reportsdynamic-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/TAILCALLwith a top-dependent argument count emitted onlyopen-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. Theoriginsandexportschemas gain the field. -
Resolve the
a or banda and bidioms 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 asunknown: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. Ontests/fixtures/origins_testset.lua,local chosen = primary or secondarymoves fromalternatives(parameter[1] | unknown:overwritten)to
alternatives(parameter[0] | parameter[1]), and a chaineda or b or c, where one hole
swallowed two parameters, moves fromalternatives(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.mdnow states whatoverwrittenmeans: 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 --helpand the unrecognized-subcommand error now name the command that answers each
reader's question, and state plainly that there is no decompiler, pointing atluad inspect
plus unluac for readable source and atdocs/examples/RECIPES.mdfor worked chains. The
subcommand list named mechanisms only, so a reader who guessedluad decompilegot 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 anUnknowncollapsed to
control-flow-conflict, discarding the definition that did resolve and flattening the
unresolved one's own reason; it now emits thealternativesset with that definition as
an option carrying its reason. Ontests/fixtures/origins.luathe root-kind histogram
movesunknown8 to 7 andalternatives3 to 4, with no origin losing an expression.
has_unknownis 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-conflictis 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