commit b0630194cc5c11d1a0c84867a89ffca9f5e00cc6
parent 8a8465281e50c54f7930b0e7e029882790b78776
Author: Jared Tobin <jared@jtobin.io>
Date: Tue, 27 Oct 2026 11:19:04 +0400
readme, changelog: data-independent-timing contracts
Diffstat:
| M | CHANGELOG | | | 17 | +++++++++++++++++ |
| M | README.md | | | 54 | +++++++++++++++++++++++++++++++++++++++++++----------- |
2 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
@@ -1,5 +1,22 @@
# Changelog
+- 0.2.0 (unreleased)
+ * Data-independent-timing contracts: `record --contract arm-dit`
+ or `intel-doitm`, with `--object` and `--objdump`, has the plugin
+ fingerprint the operands of every instruction outside the ISA's
+ contract in the objects named, as it does a division's. The
+ tables are transcribed from the vendors' statements: Arm's A64
+ ISA XML (2022-12), every encoding with its source registers, and
+ Intel's list 768547 (2026-02-23).
+ * `diff` reports such an instruction on other operands under
+ `outside`, apart from the origins, with its text, and how many
+ ran (vestigia/diff-v4, which also names the ISA and contract, and
+ gives an operand origin its text where the watch list named it).
+ Runs recorded under other contracts are refused.
+ * `record` appends the guest's ISA to its dump.
+ * `diffOf` takes a `contract`; the command finds LLVM's
+ `llvm-objdump` through `$VESTIGIA_OBJDUMP`.
+
- 0.1.0 (2026-10-22)
* Initial version, with two instruments over a harness that marks
its secret (`vestigia.h`, defined by `vestigia.c`, which serves
diff --git a/README.md b/README.md
@@ -108,19 +108,46 @@ loaded an object at different addresses. A guest reading
`/dev/urandom` still reads the host's.
`record` appends to its dump how the run was made: the program and
-its arguments, the size of the guest's environment, and the exit
-status, which the report gives for each run. `diff` refuses a run of
-another program, or with arguments of another number or length, or an
-environment of another size, than the first run's, each of which lays
-the guest's stack out apart; and a control that does not repeat the
-first run's arguments.
+its arguments, the size of the guest's environment, the exit status,
+and the guest's ISA, which the report gives for each run. `diff`
+refuses a run of another program, or with arguments of another
+number or length, or an environment of another size, than the first
+run's, each of which lays the guest's stack out apart; and a control
+that does not repeat the first run's arguments.
+
+### Under a contract: `record --contract`
+
+An ISA's data-independent-timing contract lists the instructions
+whose time does not depend on their data: Arm's `PSTATE.DIT`
+(`arm-dit`) and Intel's DOITM (`intel-doitm`), transcribed from the
+vendors' own statements (`lib/Vestigia/Contract/Arm.hs`, generated
+from Arm's A64 ISA XML, and `lib/Vestigia/Contract/Intel.hs`, from
+Intel's list, each citing its release). Under one, `record`
+disassembles the objects the program loads whose path contains an
+`--object` text (with LLVM's `llvm-objdump`: `--objdump`, else
+`$VESTIGIA_OBJDUMP`), and the plugin fingerprints the operands of
+every instruction outside the contract, as it does a division's:
+
+```
+$ vestigia record --contract intel-doitm --object /bin/probe \
+> --dump run-1 -- ./probe 00000005
+```
+
+One run equally often on other operands in some run is reported by
+`diff` apart from the origins, under `outside`, with its text: the
+contract says nothing of its time either way, so it qualifies a
+clean report rather than failing it. The report names the contract
+and the ISA, and how many instructions outside the contract ran.
+Runs compared must have been recorded under the same contract. A
+division is an operand origin, as without a contract.
### As derivations
The flake's `lib.<system>` holds `traceOf` and `diffOf`, which run
either instrument as a derivation over a list of runs; an
`x86_64-linux` run executes on any builder for that system, and its
-report is cached. `diffOf` repeats the first run as its control.
+report is cached. `diffOf` repeats the first run as its control, and takes a
+`contract` to record under.
`nix/lib.nix` holds the same for a consumer's own nixpkgs, beside
`version`.
@@ -134,7 +161,7 @@ vestigia.lib.x86_64-linux.diffOf {
```
The output holds `trace.json` (`vestigia/trace-v2`) or `diff.json`
-(`vestigia/diff-v3`), the runs kept or the plugin's dumps, what the
+(`vestigia/diff-v4`), the runs kept or the plugin's dumps, what the
harness printed, and the wall time.
## Two instruments
@@ -165,9 +192,14 @@ ppad-poly1305, and ppad-secp256k1.
* An address origin can be an address that an earlier secret branch
moved, a heap block or stack frame laid out apart, rather than one
computed from the secret.
-* Divisions alone are read for their operands. Floating-point
- division and square root, and multiplication on cores that time it
- by its operands, are left to a timing instrument.
+* Without a contract, divisions alone are read for their operands.
+ Under one, an instruction's source registers are read off its
+ encoding (AArch64) or its operands (x86_64, with the registers some
+ instructions read without naming); flags are not read, and a
+ 256- or 512-bit vector is read by its low 128 bits, all of it QEMU
+ names. A contract covers the ISA's statement, not a core's: a core
+ that does not honour it (or a deployment that does not enable it)
+ is for a timing instrument.
* A GHC harness wants `-with-rtsopts=-xr1G -V0`, against the
runtime's terabyte reservation and its interval timer. GHC calls
and returns by jumping, so its origins have no callers, and a