Skip to content

chore: cut v4.5.1 - #319

Merged
danielplohmann merged 10 commits into
masterfrom
chore/v4.5.1-release-notes
Sep 8, 2026
Merged

chore: cut v4.5.1#319
danielplohmann merged 10 commits into
masterfrom
chore/v4.5.1-release-notes

Conversation

@danielplohmann

@danielplohmann danielplohmann commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Cuts v4.5.1. No longer held for #299 and #300 — those are re-scoped to v4.6.0, so this is ready.

Everything merged so far is in and the branch carries all of master (6240b74), so it adds exactly three lines: the entry, VERSION, and __version__.

What the entry already covers

All fourteen changes since v4.5.0: #302, #303, #304, #305, #306, #307, #308, #310, #316, #318, and today's #309, #311, #312, #320, #321, plus the Dependabot ty bump #302 had to answer for.

Grouped as Recovery (intel) / Recovery (AArch64) / Defaults changed / New (default off) / Docs / Housekeeping, closing with the movement note the v4.4.7 entry established.

The release-level figures, measured on this tree

rust_pe_gnu_xored, against the 2,186 .text function symbols its mingw build retains:

v4.5.0 this branch delta
starts 2355 2201
TP 2021 2047 +26
FP 334 154 −180
recall 92.452% 93.641% +1.19pp
precision 85.817% 93.003% +7.19pp

The 11-sample ARM64 Mach-O corpus, against LC_FUNCTION_STARTS (2,056 truth functions):

v4.5.0 this branch delta
TP 1787 1818 +31
FP 1113 1107 −6
recall 86.916% 88.424% +1.51pp

No sample loses recall in either table.

Why #299 and #300 moved to v4.6.0

They were going to ride along, on this morning's plan. Two things changed it:

#299 contains #300, so it follows. Neither is blocked on a decision from here — they need a rebase and a re-measurement, both tracked on those PRs.

Changelog formatting

@r0ny123 asked on this PR whether the entry could be formatted as a list. Done in d678566: the release summary is the top-level bullet, one sub-bullet per section, and a third level inside the three sections that cover more than one change.

Only this entry changes shape — the other 192 keep the one-line format. Converting them is a large rewrite that loses nuance in translation, and #323 proposes keeping them verbatim under an Older releases heading instead. The wider keep-a-changelog adoption is tracked there, deliberately scheduled after this release so that a format change and release content are not reviewed in the same pass.

The text is unchanged, and that was verified mechanically rather than by eye: stripping list markers and collapsing whitespace reproduces the original entry exactly. Two opening letters were capitalised, where splitting a section header onto its own line left a bullet starting mid-sentence.

Still manual after merging

There is no release automation in this repo — no workflow publishes on a tag. Merging this lands the entry and the version bump; tagging v4.5.1 and publishing remain manual.

Carried caveats

Two figures are marked as the contributor's own because nothing here can reproduce them, and #322 tracks closing that gap:

Both mechanisms were verified directly, so this is about magnitudes rather than correctness.

Also recorded: @r0ny123 withdrew #304's built-Rust precision figure after reproducing this side's measurement cell for cell. It never reached the changelog — only the pooled 191-sample numbers did, which stand.

And a note for whoever reads a benchmark verdict into this entry: the corpus gate's correctness half is solid (it cleared #320 and #321 at 0 of 155 files differing over 175,946 functions), but its timing half called two behaviour-neutral typing changes −3.70% and +14.78%. Cross-runner variance, tracked in #322. Do not quote a speedup from it.

Validation

pre-commit run clean on all three files. Full suite on this tree: 1958 passed, 2 skipped, 2593 subtests. smda.__version__ == SmdaConfig.VERSION == "4.5.1", which testIdaExporterReuse asserts. ESCAPER_DOWNWARD_COMPATIBILITY stays at 4.4.5 and INTEL_PIC_HASH_ESCAPE_VERSION at 4.3.5 — no escaper output changed, so nothing needs reprocessing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MDCLUmqGzE5fwJqUWsSStr

danielplohmann and others added 3 commits September 7, 2026 17:04
The changelog entry for what has landed since v4.5.0 (6cc5aca): #302, #303,
#304, #305, #306, #307, #308, #310 and #316, plus the Dependabot ty bump
572acd7 that #302 had to answer for.

Held as a draft rather than a release. The version is deliberately NOT
bumped here -- #309, #311 and #312 are reviewed and wanted but waiting on a
rebase, and splitting one coherent accuracy pass across two releases makes
both entries weaker. What this commit does is stop the writeup living in a
scratchpad while that happens.

To finish it: add sections for #309, #311 and #312, bump VERSION in
src/smda/SmdaConfig.py and __version__ in src/smda/__init__.py, and move the
date to the actual release day.

No escaper output changed anywhere in this set -- the only edit to
intel/definitions.py adds two GAP_SEQUENCES entries, which feed padding
detection and not escaping, and testEscaperFingerprint passes unchanged --
so ESCAPER_DOWNWARD_COMPATIBILITY stays at 4.4.5 and
INTEL_PIC_HASH_ESCAPE_VERSION at 4.3.5, and no report needs reprocessing.

Figures are the contributors' own except where this session reproduced them,
and the two that were reproduced are stated as measured here: the padding
cut's analysis-time cost (+3.4% claimed, +3.84% measured) and the fixture
movement on rust_pe_gnu_xored (+26 real starts against +8 false, scored
against that fixture's own COFF symbol table rather than the corpus macro
mean).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#318 landed as 395c88d after this entry was written. Its five corrections go
under Housekeeping: the README's metadata.language claim, the restored
`-> bool`, the ruff-check hook rename, the GAP_SEQUENCES reach note, and the
docstring on the queue-rebuild test.

Keeping this current as things merge is the point of the branch existing, so
it does not go stale between now and the release.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

📊 SMDA Performance Evaluation Benchmark Results

Generated on: 2026-09-08 21:22:17

Summary

Metric Result
Correctness ✅ PASS — 0 / 155 common file(s) differ
Determinism ✅ PASS — base 3 run(s), PR 3 run(s)
Verdict PR is faster
Median paired speedup +16.45% (95% CI [+15.31%, +17.31%])
Timing noise band ±4.6%

Per-side Timing Context (best of 3/3 runs per file)

Side Files Functions Median best time/file (s) Sum of best times (s) Throughput estimate (func/s)
base 155 175946 0.8170 306.20 ~575
pr 155 175946 0.6393 258.54 ~681

These context rows sum each file's best observed time across repeated runs; they are normalized comparison estimates, not single-run CI wall-clock times.

Paired per-file timing (positive speedup = PR faster):

Statistic Value
Files compared 155
Median paired speedup +16.45% (95% CI [+15.31%, +17.31%])
Mean speedup +17.00% (95% CI [+15.49%, +18.47%])
Std dev / IQR 9.55% / 7.94%
Wilcoxon signed-rank p 0.0000 (n=155)

ℹ️ base and PR are timed on separate CI runners, so a small median difference can reflect per-runner hardware variance rather than code. Differences within the timing noise band above are reported as inconclusive; correctness and determinism are unaffected (they are not timing-based).

Determinism (self-check across repeated runs)

Side Runs Files Deterministic Median timing CV
base 3 155 4.4%
pr 3 155 4.6%
Pairwise run matrix (individual run medians, diagnostic)

Diagnostic only: each row compares one raw PR run against one raw base run. The headline verdict above uses paired per-file best-of-runs timings.

Comparison Run PR Files Base Files Common Function Set Matches Med PR (s) Med Base (s) Med Diff (s) Speedup %
pr_0 vs base_0 155 155 155 155/155 0.8092s 0.9205s -0.1113s +12.09%
pr_0 vs base_1 155 155 155 155/155 0.8092s 0.9256s -0.1164s +12.58%
pr_0 vs base_2 155 155 155 155/155 0.8092s 0.9228s -0.1136s +12.31%
pr_1 vs base_0 155 155 155 155/155 0.8107s 0.9205s -0.1098s +11.92%
pr_1 vs base_1 155 155 155 155/155 0.8107s 0.9256s -0.1149s +12.41%
pr_1 vs base_2 155 155 155 155/155 0.8107s 0.9228s -0.1121s +12.14%
pr_2 vs base_0 155 155 155 155/155 0.8116s 0.9205s -0.1089s +11.83%
pr_2 vs base_1 155 155 155 155/155 0.8116s 0.9256s -0.1140s +12.32%
pr_2 vs base_2 155 155 155 155/155 0.8116s 0.9228s -0.1112s +12.05%

danielplohmann and others added 2 commits September 8, 2026 12:42
Finishes the draft now that #309, #311 and #312 have landed. Adds their
sections, bumps `VERSION` and `__version__` to 4.5.1, and moves the date to the
release day.

The three new entries: #309's exception-directory refusal of interior gap
candidates on x64, with the chained-versus-primary distinction and the
seeding-is-not-suppression principle that keeps carved records out of it; #311's
four candidate-quality defects, including the corrected entry at `0x40df30` on
`aarch64_static_xored` and the metadata-coverage gate; and #312's ARM64
counterpart, which reconstructs the extent an ARM64 `RUNTIME_FUNCTION` does not
carry and hoists the shared lookup into `common/`.

`USE_MACHO_ADDRESS_REF_CANDIDATES` gets its own **Defaults changed** section. It
is the only default whose value moves in this release, it changes recovery
output on Mach-O input with no action by the caller, and a default going from
off to on inside a follow-ups PR is exactly the kind of thing that gets lost.

The closing movement section is rewritten to describe v4.5.0 -> v4.5.1 rather
than any single PR, and both figures in it were measured on this tree against
independent truth rather than quoted:

  rust_pe_gnu_xored, vs its 2,186 retained COFF .text function symbols
    2,355 -> 2,201 starts, +26 real / -180 false
    recall    92.452% -> 93.641%
    precision 85.817% -> 93.003%

  11-sample ARM64 Mach-O corpus, vs LC_FUNCTION_STARTS (2,056 truth)
    TP 1,787 -> 1,818, FP 1,113 -> 1,107
    recall 86.916% -> 88.424%, no sample losing recall

Two headline figures are marked as the contributor's own because they cannot be
reproduced here at all: the `bti j` 803/0 split, since no bundled fixture holds
a single `bti j` word, and #312's ARM64 PE result, since a scan of all 104
fixture files finds 8 i386 PEs, 4 AMD64, one ReadyToRun image and no `0xAA64`.
Saying which figures are ours and which are theirs is more useful than a uniform
tone of confidence.

`Turtle_5f9cd91d8d1d`'s 12 dropped starts are attributed rather than left as an
unexplained delta: they bisect to #307's inbound-call fix and fall inside the
175 false positives that change already measured, and the sample carries no
LC_FUNCTION_STARTS to score them against individually.

Closes #319

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDCLUmqGzE5fwJqUWsSStr
@danielplohmann
danielplohmann marked this pull request as ready for review September 8, 2026 10:46
@danielplohmann danielplohmann changed the title chore: draft the v4.5.1 release notes chore: cut v4.5.1 Sep 8, 2026
danielplohmann and others added 4 commits September 8, 2026 12:48
Left open as a question in yesterday's review and resolved as "yes, it belongs
in the entry". #302 moved `SmdaFunction.binweight`'s class default from int `0`
to float `0.0` while clearing ty 0.0.74's diagnostics, and `binweight` is
serialized through `toDict()`.

The blast radius is small but real: the per-block accumulation already adds
`float(...)`, so every function with at least one block was a float before this
release too. Only a function with no blocks -- a zero-function or error report
-- keeps the class default, and that value now writes as `0.0` where it wrote
`0`. Verified on both trees rather than reasoned about: `SmdaFunction.binweight`
is `0` at 6cc5aca and `0.0` at d111548.

MCRIT stores these reports, so a consumer diffing them byte-for-byte will see
it even though nothing reads the field as an integer. That is exactly the kind
of change that costs someone an afternoon if it is not written down.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDCLUmqGzE5fwJqUWsSStr
#320 and #321 finish the review pass that #318 started, so the release entry
should carry all seven of its corrections rather than five.

Both are behaviour-neutral typing work, but the Housekeeping text says what each
one actually decided rather than listing them as annotations: `blocks` being
typed forces the PIC/OPC hash path to refuse an instruction with no bytes
instead of blanking it, and `extract_strings` could not be narrowed until three
helpers and three `SmdaReport` attributes were declared first.

Behaviour-neutrality is stated with the evidence rather than as a claim -- the
corpus benchmark ran on #320 and reports 0 of 155 files differing across
175,946 functions, which is a stronger statement than the bundled fixtures can
make.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDCLUmqGzE5fwJqUWsSStr
@danielplohmann
danielplohmann marked this pull request as draft September 8, 2026 12:04
@danielplohmann

Copy link
Copy Markdown
Owner Author

Back to draft. #309, #311 and #312 landed this morning and their sections are in, along with #320 and #321 — but #299 and #300 are being taken in a later session and the release waits for them, so this stops short of the merge for the same reason it did yesterday.

The branch carries all of master (6240b74) and adds three lines: the entry, VERSION, __version__.

One thing worth flagging for whoever picks this up rather than leaving it to be discovered: the closing movement section goes stale when #300 merges. It changes function-boundary decisions, so both scored tables in the body — rust_pe_gnu_xored against its COFF symbols, and the ARM64 Mach-O corpus against LC_FUNCTION_STARTS — need re-measuring against the final tree, not amending. #299 claims no output change, which the corpus benchmark's correctness half can confirm directly.

@r0ny123

r0ny123 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

@danielplohmann Could this changelog be formatted as a list to make it easier to read? Also, could we use https://github.com/olivierlacan/keep-a-changelog across the mcrit ecosystem?

@r0ny123 asked on #319 whether the changelog could be formatted as a list to
make it easier to read. At ~2,700 words on a single line, this entry was the one
that provoked the question, so it gets the treatment now rather than waiting for
the wider keep-a-changelog adoption tracked in #323.

Deliberately narrow: only the v4.5.1 entry changes shape. The other 192 entries
keep the one-line format, because converting them is a large, low-value rewrite
that loses nuance in translation, and #323 proposes preserving them verbatim
under an `Older releases` heading instead.

Structure is the release summary as the top-level bullet, one sub-bullet per
section, and a further level inside the three sections that cover more than one
change -- Recovery (intel) splits into the switch-table fix, the alignment cut
and the exception-directory refusal; Recovery (AArch64) into the inbound-call
fix, the BTI work and the candidate-quality follow-ups; Housekeeping into the
`ty` bump, the `ruff` pin, the five #318 corrections, the `binweight`
serialization note and the two #317 typing items.

The text is unchanged. Verified mechanically rather than by eye: stripping list
markers and collapsing whitespace gives a string identical to the original entry.
The two exceptions are disclosed rather than silent -- the first sub-bullet of
Recovery (intel) and of Recovery (AArch64) had their opening letter capitalised,
because splitting the section header onto its own line left them starting a
bullet mid-sentence. Housekeeping's opens on a code span and needed nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDCLUmqGzE5fwJqUWsSStr
@danielplohmann
danielplohmann marked this pull request as ready for review September 8, 2026 21:12
@danielplohmann

Copy link
Copy Markdown
Owner Author

@r0ny123 both of your questions, answered separately:

Formatting as a list — done in d678566. The release summary is the top-level bullet, one sub-bullet per section, and a third level inside the three sections that cover more than one change (Recovery (intel), Recovery (AArch64), Housekeeping). At ~2,700 words on one line this entry was the one that provoked the question, so it seemed right to fix it here rather than make you wait for the wider change.

Only this entry changed shape; the other 192 keep the one-line format for now. The text is untouched — verified by stripping the list markers and collapsing whitespace, which reproduces the original string exactly, rather than by reading it back. Two opening letters were capitalised where splitting a section header onto its own line left a bullet starting mid-sentence.

keep-a-changelog across the ecosystem — yes, and the decision is already made on our side for SMDA and MCRIT both, so the two stay consistent. #323 has the details and asks for your input on the parts that are genuinely open. Four of them would benefit most from your view:

  • version-order vs date-order, given three releases in this file are patches on an older line published after a newer minor
  • whether to keep the per-backend split (Recovery (intel) / Recovery (AArch64)) as a bullet prefix under the spec's change-type headings, since mapping literally collapses six sections into mostly Fixed
  • a custom Compatibility section for the "recovery output moves" notice, which the spec has no slot for
  • the bullet convention that preserves the depth, which is the one I would most like agreed at the same time as the format. The structure will not keep the measurement and the caveat attached by itself, and if we do not write the convention down the substance quietly drains out of these entries. You write most of the source material, so it has to be one you would actually want to follow.

One thing #323 records that surprised me while checking: the spec's header template asserts semver, and SMDA does not follow it — v4.3.4 shipped a whole new default-off pass as a patch, v3.0.2 added two config options as a patch, and v4.0.0 was an addition rather than a break. Worth stating the real scope instead, which in this project is ESCAPER_DOWNWARD_COMPATIBILITY and the *_PIC_HASH_ESCAPE_VERSION markers — those are what MCRIT actually reads.

Unrelated to both: #299 and #300 are now aimed at v4.6.0 rather than this release. Details on those PRs; the short version is that #300's corpus table predates #304/#307/#309/#310/#311 and needs re-running before it can be read, and ten engine changes under feat(core) is a minor-bump shape anyway. No rush on either — better measured than fast.

@danielplohmann
danielplohmann merged commit 4868e19 into master Sep 8, 2026
42 checks passed
@danielplohmann
danielplohmann deleted the chore/v4.5.1-release-notes branch September 8, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants