Skip to content

chore(uniffi): port upstream generator fixes into the forks - #7461

Merged
kixelated merged 11 commits into
mainfrom
quest/m1/vendor-sync/uniffi
Nov 2, 2026
Merged

kixelated merged 11 commits into
mainfrom
quest/m1/vendor-sync/uniffi

Conversation

@kixelated

@kixelated kixelated commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The recurring UniFFI audit found correctness fixes missing from MoQ's pinned generators and from rust2's imports: Python awaits a call on the configured fallback loop instead of the running one, and Dart leaks input scratch or loses a native handle when an allocation fails.

Approach

  • Pin the reviewed fork ports, both now landed on moq-0.32 as merge commits: rs/uniffi-bindgen to kixelated/uniffi-rs#1's head 35209f3f, and the Nix Dart generator plus dart/README.md to tag v0.3.1-kixelated.7+v0.32.0, which points at kixelated/uniffi-dart#7's head 657cbff5.
  • Add the fork's new input_scratch fixture to nix/uniffi-dart-Cargo.lock (MoQ's three Dart patches still apply) and regenerate dart/moq_ffi.
  • The root Cargo.lock keeps main's resolution and moves only the uniffi fork rev (88d835d → 35209f3, 24 lines). An earlier rewrite here rolled back main's windows-sys/socket2/getrandom upgrades; pullfrog flagged it and the merge of main restored them.
  • Select the py, kt, and swift checks for rs/uniffi-bindgen/ changes in sh/dispatch.sh. Those bindings render through that crate, but a pin bump there matched none of their modules, so CI would have checked none of them.
  • Rewrite the quest Plan to this run's checked state, which the next run carries forward, plus what remains: the fork CI work main added to this quest in moq-dev/moq#7568.

moq-dev/rust2#364 (merged) mirrors both ports, so the rust2 switch keeps them.

Impact

  • Public API: none. Generated Dart changes only toRustBuffer and the 50 dispose() bodies; Python's generated Async.py prefers the running loop. Kotlin, Swift, Go, and C++ output is unchanged.
  • Wire and UniFFI metadata contract: unchanged.

Alternatives

Do not adopt upstream main's parser/JNI/generator redesign. Go and C++ upstream heads have not changed; Kotlin's checksum fix is already in the pin. The quest records why callback/custom-type/default/recursion changes have no current generated MoQ consumer. No dependency bumps.

Decisions

Settled by the maintainer on 2026-11-02:

  1. ✅ Land fix(python): prefer the running event loop kixelated/uniffi-rs#1 and fix(dart): retain ownership through allocation failures kixelated/uniffi-dart#7 with a merge commit, not a squash. Done: merge commits c2593169 and ce8fa314. GitHub's compare shows 35209f3f and 657cbff5 0 commits behind moq-0.32. uniffi-rs#1 had no checks. uniffi-dart#7's Testing (futures_test.dart: sleep) and Test Downstream (bdk-dart, rust-payjoin) failures match the same failures on the fork's earlier PRs (for example the error-display run on 2026-09-29), while Nix and Lints passed.
  2. ✅ Tag 657cbff5 as v0.3.1-kixelated.7+v0.32.0 (annotated, like .6) and switch flake.nix (rev = "v${version}") and dart/README.md back to the tag. The prefetched tag hash is sha256-uSApyCocEhADWu8Koj5vXIt0fMYQVjnBVKctDWQHqLM=, unchanged.
  3. ✅ Merge main into this PR (no rebase), finish it, and mark it ready. Landing stays with /quest-merge.

Choice made without asking: main's moq-dev/moq#7568 added the uniffi-dart fork-CI cleanup to this quest. It needs its own fork PR and merge, so this PR leaves it as the quest's remaining work instead of deleting the quest. That also keeps this run's checked commits in the quest for the next run.

Validation

  • quest gate nix develop -c just ci origin/main on e1a8bab2 (after merging main at 608e6342): every selected module passes, including rs, py, kt, go, c, cpp, obs_compile, js, gh, quest, the rs/py/js/gh tests, and dart, which regenerates dart/moq_ffi from the tagged generator and leaves the tree clean. The exception is check flake. It fails building nixpkgs' github-runner (unshare: write failed /proc/self/uid_map: Operation not permitted) on this host, and checks.x86_64-linux.github-runner-vm evaluates to the same 4izikpkp…-vm-test-run-github-runner-vm.drv on origin/main and this head, so this PR doesn't cause it.
  • cargo metadata --locked accepts the merged Cargo.lock. git diff origin/main -- Cargo.lock is only the uniffi rev lines.
  • Earlier round, before the tag switch: wrapper checks CHECK_MODULES="py kt swift go c cpp dart obs_compile" just ci --all all passed (swift skips off macOS), just test interop --all gave 183 PASS / 0 FAIL, and sh/dispatch-query.test.py's new generator-pin selection test passed.

CI

The Check failure on e1a8bab2 (run 41497117863) is a pre-existing use-after-free in a moq-c test on main. This PR doesn't cause it.

  • Symptom. moq-c test::follow_delivers_the_instance_again_after_an_end printed test ... ok, then the process aborted: corrupted size vs. prev_size (SIGABRT). That is glibc detecting heap corruption after the test body passed.
  • Cause, from the code. gated_callback (rs/moq-c/src/test.rs, added in moq-dev/moq#5886) runs on the runtime thread. It sends code on gate.tx, and only then locks gate.hold. For the terminal 0 after moq_origin_follow_cancel, the test thread can receive that 0 and run drop((run, dynamic, served, gate)) while the callback is still about to lock the mutex inside the freed Box<Gate>. moq_origin_request's contract (rs/moq-c/src/api.rs:2384) keeps user_data valid only until the terminal callback, so the test frees it too early.
  • Reproduction on origin/main (5e327b55).
    • I widened the window with a temporary, uncommitted 20 ms sleep between the send and the lock and ran the test under valgrind. It reports Invalid read/Invalid write of size 8 in moq::test::gated_callback, at addresses 16..32 bytes inside a block of size 40 free'd. The freeing stack is drop_glue::<Box<moq::test::Gate>> from the test's drop((run, dynamic, served, gate)).
    • Unmodified, the window is narrow: 300 sequential runs and 5 valgrind runs on an idle host all passed. CI hits it under load with 4 tests in parallel.
  • Not this PR. cargo tree -p moq-c -e normal,dev,build contains no uniffi crate. This PR's root Cargo.lock diff only moves the uniffi fork rev. The rest of the diff is Dart, Nix, sh/, and quest files, none of which moq-c builds.
  • moq-dev/moq#7489 keeps the same gated_callback, so it doesn't fix this. The fix is to move gate.hold.lock().unwrap().take() before gate.tx.send(code), so the callback touches nothing in Gate after its last send. That's left as a follow-up outside this PR's scope.

I reran the failed job without waiting on it. The other failed Check (run 41497068881) was cancelled when a newer push superseded it.

Review

  • This branch: Codex / gpt-6.1-sol / medium, quest review --base origin/main, round 1 (new session) on 71b8425702b64178d7fa6611338724d31b2d487d: 0 findings. The head e1a8bab2ec482f76edcbfdc3395bb32fd0d9a016 adds only a conflict-free merge of main, so per the guide that round still covers it, and the checks above ran on it.
  • Automated findings: pullfrog's Cargo.lock rollback is fixed (replied and resolved). CodeRabbit and the OpenAI review reported nothing actionable.
  • Fork ports (Codex-authored): Claude Code / claude-opus-5-5 reviewed fix(python): prefer the running event loop kixelated/uniffi-rs#1 35209f3f, fix(dart): retain ownership through allocation failures kixelated/uniffi-dart#7 657cbff5, and rust2#364 832a146a directly. Each matches its upstream commit apart from naming and comments. No findings; the records are in each PR body.

Follow-ups

  • Fix the moq-c test use-after-free described under CI: gated_callback should take hold before sending. It is the only test callback that touches user_data after a send; channel_callback, state_callback, and protocol_callback send last.
  • The quest's remaining fork-CI work (kixelated/uniffi-dart Testing and Test Downstream) is already planned in the quest; its next PR is a kixelated/uniffi-dart PR.
  • check flake can't build nixpkgs' github-runner on hosts where the Nix sandbox can't write uid_map. The existing MOQ_USERNS_UNAVAILABLE probe in sh/dispatch.sh passes there, so the local gate fails rather than skipping. Worth a quest to make the probe match what the build needs.
  • A rust2 clone under a MoQ checkout picks up MoQ's root clippy.toml, which fails rust2's Clippy with disallowed_types; scratch clones of other workspaces need an empty clippy.toml above them, or should live elsewhere.

(Written by Claude Opus 5.5)

Record checked upstream heads, reproduced fixes, tested fork and rust2 drafts, and the local Cargo/review blockers. Leave pin integration and the quest unfinished.

Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
@kixelated kixelated changed the title UniFFI generator forks are current chore(uniffi): checkpoint vendor audit and blocked ports Oct 31, 2026
kixelated and others added 5 commits October 31, 2026 22:46
Point the Python/Kotlin/Swift generator at kixelated/uniffi-rs#1's head
(running-loop preference) and the Dart generator at
kixelated/uniffi-dart#7's head (allocation-failure ownership), add the new
fixture to the Nix lock data, and regenerate the committed Dart bindings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pin bump in rs/uniffi-bindgen changes the Python, Kotlin, and Swift
bindings, but matched none of their modules, so CI checked none of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated and others added 2 commits November 1, 2026 21:05
# Conflicts:
#	quest/m1/vendor-sync/uniffi.md
kixelated/uniffi-dart#7 landed on moq-0.32 and its head 657cbff5 is now
tagged, so the flake and README go back to the fork's tag convention.
The source hash is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title chore(uniffi): checkpoint vendor audit and blocked ports chore(uniffi): port upstream generator fixes into the forks Nov 2, 2026
@kixelated
kixelated marked this pull request as ready for review November 2, 2026 06:01

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit: 1f24fa1aa182e273674d30a51798723361ef37cb.

No actionable bugs found. The direction is sound: port the focused fixes in the generator sources rather than patch each binding or adopt the larger upstream redesign. Python now selects the loop actually awaiting the call; Dart reserves disposal status before relinquishing ownership and unwinds scratch allocations in reverse order. The dispatcher change covers the Python/Kotlin/Swift consumers of the pinned generator, with a targeted selection test. No new public API or wire surface was identified.

Verification: static GitHub inspection of the PR, pinned fork changes, runtime context, and binding-check consumers. No prior published review to carry forward. I did not run generation, builds, tests, fault-injection cases, or Nix checks; the PR's reported results were not independently reproduced.

(Written by OpenAI)

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The root Cargo.lock change does more than move the uniffi rev: it rolls main's dependency upgrades (windows-sys, socket2, getrandom) back across ~17 crates. Details inline.

Reviewed changes

  • Fork pin bumps: rs/uniffi-bindgen/Cargo.toml and the root Cargo.lock move the uniffi rev 88d835d -> 35209f3; flake.nix and dart/README.md move the Dart generator to tag v0.3.1-kixelated.7+v0.32.0.
  • Dart bindings regenerated: toRustBuffer now nests the ForeignBytes alloc in try/finally, and each of the 50 dispose() bodies allocates and frees its own RustCallStatus, keeping the finalizer attached when calloc fails.
  • Nix lock fixture: nix/uniffi-dart-Cargo.lock gains the fork's input_scratch test package.
  • CI selection: sh/dispatch.sh routes rs/uniffi-bindgen/ changes to the py/kt/swift checks, covered by a new sh/dispatch-query.test.py case.
  • Quest: quest/m1/vendor-sync/uniffi.md is rewritten to the checked pins and the remaining fork-CI work.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Comment thread Cargo.lock Resolved
@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f9547274-9a85-4a7a-9900-3e625ad0c11e
Commits

Reviewing files that changed from the base of the PR and between d52d55a and 1f24fa1.

Files ignored due to path filters: 2
  • Cargo.lock is excluded by !**/*.lock
  • nix/uniffi-dart-Cargo.lock is excluded by !**/*.lock
Files selected for processing: 8
  • dart/README.md
  • dart/moq_ffi/lib/src/moq.dart
  • dart/moq_ffi/lib/src/uniffi_runtime.dart
  • flake.nix
  • quest/m1/vendor-sync/uniffi.md
  • rs/uniffi-bindgen/Cargo.toml
  • sh/dispatch-query.test.py
  • sh/dispatch.sh

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 1 remain after this review.

Walkthrough

Dart buffer conversion now releases its temporary allocation separately from the backing byte buffer. Wrapper disposal methods check native free-call status and release the status allocation. The UniFFI bindgen and Dart generator pins are updated. CI dispatch now selects Python, Kotlin, and Swift checks for bindgen manifest changes, with a test covering that selection. Vendor-sync documentation records fork updates and CI findings.

Merge Risk: Minimal · up to 1f24f

No identified issue blocks merging after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly identifies the main change: porting upstream UniFFI generator fixes into the project forks.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description directly explains the UniFFI generator pin updates, Dart ownership fixes, regenerated bindings, CI dispatch changes, validation results, and known pre-existing failures. It is clearly …
Full details: Description check

Explanation

The description directly explains the UniFFI generator pin updates, Dart ownership fixes, regenerated bindings, CI dispatch changes, validation results, and known pre-existing failures. It is clearly related to the changeset.

Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 1 functions across 2 files. (6 skipped: 6 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

kixelated and others added 2 commits November 2, 2026 09:29
…uniffi

Resolve Cargo.lock from main and move only the uniffi fork rev, keeping
main's dependency versions (windows-sys, socket2, getrandom, toml).
Resolve sh/dispatch.sh to main's module() layout with the uniffi-bindgen
patterns re-applied to py, kt, and swift.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uniffi

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit: e1a8bab2ec482f76edcbfdc3395bb32fd0d9a016.

No new actionable bugs found. The unrelated lockfile rollback identified in Pullfrog's finding is fixed: relative to the merged base, Cargo.lock now changes only the UniFFI fork revision, retaining main's dependency versions. The generator pin, generated Dart cleanup changes, and selection test remain unchanged; the dispatcher migration preserves Python/Kotlin/Swift generator coverage.

Direction remains sound: preserve main's lockfile and apply the targeted fork update rather than re-resolving unrelated dependencies. Compared with the previous published review and accounted for base merges. Verification was static GitHub inspection; no generation, builds, tests, cargo metadata, or Nix checks were run.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

This run re-reviewed the two commits since the prior pullfrog review (1f24fa1aa), both merges of main.

  • Restored the root Cargo.lock to main. The windows-sys/socket2/getrandom rollback flagged in the prior review is gone; git diff origin/main -- Cargo.lock is now exactly the 24-line uniffi rev swap (88d835d → 35209f3) across the ten uniffi/weedle2 package entries, and the two re-fetched crates keep toml 1.1.6.
  • Restored rs/uniffi-bindgen/Cargo.toml's dev-dependency. The branch-only [lints] workspace = true that had replaced [dev-dependencies] workspace-hack is gone, so the manifest matches main apart from the uniffi rev.
  • Rewrote the quest Plan to the checked pins and the remaining fork-CI work.
  • Clarified the sh/dispatch.sh binding comment to name which bindings render through the generator fork (no behavior change).

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@kixelated
kixelated added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit 169a825 Nov 2, 2026
11 of 14 checks passed
@kixelated
kixelated deleted the quest/m1/vendor-sync/uniffi branch November 2, 2026 21:05
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.

1 participant