Skip to content

Conversation

@alltheseas
Copy link
Collaborator

@alltheseas alltheseas commented Feb 2, 2026

Summary

Fixes a use-after-free crash affecting 7 devices in TestFlight v1.16 Build 1277.

ByteBuffer(assumingMemoryBound:capacity:) creates an unowned buffer pointing directly to LMDB's memory-mapped region. When the transaction closes, this memory becomes invalid but the ProfileRecord still references it, causing crashes when accessed later.

Changed to ByteBuffer(memory:count:) which copies the data into an owned buffer that remains valid after the transaction closes.

Closes #3560

Checklist

Standard PR Checklist

  • I have read (or I am familiar with) the Contribution Guidelines
  • I have tested the changes in this PR
  • I have profiled the changes to ensure there are no performance regressions, or I do not need to profile the changes.
    • Not needed: Minor memory copy during profile lookup, no measurable performance impact
  • I have opened or referred to an existing github issue related to this change.
  • My PR is either small, or I have split it into smaller logical commits that are easier to review
  • I have added the signoff line to all my commits. See Signing off your work
  • I have added appropriate changelog entries for the changes in this PR. See Adding changelog entries
    • I do not need to add a changelog entry. Reason: Bug fix for crash, no user-facing feature change
  • I have added appropriate Closes: or Fixes: tags in the commit messages wherever applicable, or made sure those are not needed. See Submitting patches

Test report

Device: iPhone 17 Simulator

iOS: 26.2

Damus: fix/yjw-use-after-free branch (d888490)

Setup: Fresh database with test wire events

Steps:

  1. Write profile data to database
  2. Reopen database and lookup profile
  3. Close database to invalidate LMDB memory mappings
  4. Access profile fields after close

Results:

  • PASS
  • All 13 NdbTests pass including new test_profile_buffer_ownership

Testing directions for reviewers

  1. Unit tests: Run NdbTests target - test_profile_buffer_ownership specifically tests that profile data remains valid after database close
    xcodebuild test -project damus.xcodeproj -scheme damus -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:damusTests/NdbTests
  2. Code review: Verify profile_flatbuf_to_record in Ndb.swift now uses ByteBuffer(memory:count:) instead of ByteBuffer(assumingMemoryBound:capacity:)

Other notes

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved memory safety when handling profile data across database operations
    • Enhanced transaction initialization to ensure proper reference counting and state management
  • Tests

    • Added test coverage for profile data ownership and integrity validation

✏️ Tip: You can customize this high-level summary in your review settings.

alltheseas and others added 3 commits February 1, 2026 13:59
Replace force unwraps (`as!`) with safe optional binding (`as?`) in
NdbTxn and SafeNdbTxn transaction inheritance logic. This prevents
crashes when thread-local storage is in an unexpected state.

Changes:
- Move `ref_count` check into the `if let` conditional binding
- Use already-bound `txn_generation` instead of re-reading with force unwrap
- Simplify ref count increment to single expression

The fix ensures transaction inheritance gracefully falls back to
creating a new transaction if thread-local state is incomplete,
rather than crashing on missing values.

Changelog-Fixed: Fixed potential crash in NdbTxn when thread-local storage is incomplete

Signed-off-by: alltheseas
ByteBuffer(assumingMemoryBound:capacity:) creates an unowned buffer
pointing directly to LMDB's memory-mapped region. When the transaction
closes, this memory becomes invalid but the ProfileRecord still
references it, causing crashes when accessed later.

Changed to ByteBuffer(memory:count:) which copies the data into an
owned buffer that remains valid after the transaction closes.

Closes damus-io#3560

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
Verifies that profile data remains valid after the database is closed.
The test writes profile data, reopens the database, looks up the
profile, closes the database, then accesses profile fields.

This would crash with use-after-free if the buffer wasn't properly
copied to owned memory.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: alltheseas <alltheseas@users.noreply.github.com>
@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

📝 Walkthrough

Walkthrough

This pull request addresses memory safety issues in database operations by implementing proper buffer ownership to prevent use-after-free crashes when LMDB transactions close, fixing transaction reference counting patterns, and adding validation tests. Includes a new metadata configuration file.

Changes

Cohort / File(s) Summary
Configuration
.beads/metadata.json
New metadata file defining database filename (beads.db) and JSON export output (issues.jsonl).
Memory Safety Fix
nostrdb/Ndb.swift
Modified profile_flatbuf_to_record to copy flatbuffer data into owned buffer instead of assuming memory validity after LMDB transaction closure, preventing use-after-free.
Transaction Reference Counting
nostrdb/NdbTxn.swift
Updated transaction initialization for both NdbTxn<T> and SafeNdbTxn<T: ~Copyable> to read and use ref_count and txn_generation instead of force-casting, improving reference count management.
Memory Ownership Test
nostrdb/Test/NdbTests.swift
Added test_profile_buffer_ownership to verify copied profile data remains accessible after LMDB transaction and database closure, validating use-after-free prevention.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A buffer once borrowed from maps now owned,
With copy-made safety and ref_counts honed,
No use-after-free shall our data accost,
The LMDB's closure won't leave us lost!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'ndb: copy profile buffer to prevent use-after-free' clearly and concisely summarizes the main change: fixing a use-after-free crash by changing how profile buffers are handled.
Description check ✅ Passed The description comprehensively follows the Standard PR Checklist template with all required sections completed, including summary, testing details, and verified checklist items.
Linked Issues check ✅ Passed The PR fully addresses issue #3560 by eliminating unsafe memory access patterns: it replaces ByteBuffer(assumingMemoryBound:capacity:) with ByteBuffer(memory:count:) to copy data into owned buffers, preventing use-after-free when transactions close.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the use-after-free issue: profile buffer handling in Ndb.swift, transaction inheritance patterns in NdbTxn.swift to support safer fixes, and a unit test verifying the fix. The metadata.json appears to be an unrelated build artifact.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@alltheseas
Copy link
Collaborator Author

@coderabbitai review please

@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

@alltheseas: I'll review the changes in this PR now.


🧠 Learnings used
Learnt from: CR
Repo: damus-io/damus PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T01:12:51.605Z
Learning: Code should be human reviewable
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@alltheseas alltheseas requested review from danieldaquino and jb55 and removed request for danieldaquino February 2, 2026 14:40
@danieldaquino danieldaquino added the pr-short Pull request with less than 100 lines of code changed label Feb 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-short Pull request with less than 100 lines of code changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TestFlight Build 1277 Crash Report Analysis

2 participants