Merge 'Fix io_uring WAL write corruption by ensuring buffer lifetime' from Daniel Boll

### Problem
When using the `io_uring` backend, WAL file writes were corrupted: the
submitted buffer data (e.g., WAL header magic `37 7f 06 82`) was correct
in logs, but the file on disk showed incorrect data (e.g., `00 18 27
xx`). This occurred because the `Arc<RefCell<Buffer>>` was dropped
before the asynchronous `io_uring` write completed, allowing the kernel
to write stale or freed memory.
### Root Cause
In `UringFile::pwrite`, the `buffer` was passed to `io_uring` via an
`iovec`, but the `Arc<RefCell<Buffer>>` wasn’t guaranteed to live until
the write finished. Unlike synchronous `UnixIO`, where the buffer
persists during the `pwrite` call, `io_uring`’s async nature exposed
this lifetime issue.
### Fix
Modified `UringFile::pwrite` to hold a reference to the `buffer` in the
completion callback by calling `buffer.borrow()`. This ensures the
`Buffer` remains alive until `io_uring` completes the write, preventing
memory corruption.
### Changes
- Updated `core/io/io_uring.rs`:
  - Added `WriteCompletion` import.
  - Wrapped the original `Completion` in a new `WriteCompletion` closure
that references the `buffer`, extending its lifetime until the write
completes.
### Validation
- Tested with `limbo -v io_uring`:
  - `.open limbo.db`
  - `CREATE TABLE users (id INT PRIMARY KEY, username TEXT);`
  - `INSERT INTO users VALUES (1, 'alice');`
  - `INSERT INTO users VALUES (2, 'bob');`
  - `SELECT * FROM users;`
- Verified WAL file with `xxd -l 16 limbo.db-wal`:
  - Before: `0018 2734 ...`
  - After: `377f 0682 ...` (correct WAL magic).
- `wal-browser limbo.db-wal` confirms the header is written correctly,
**though frame checksums still need separate fixing** (tracked in a
follow-up issue).
### Follow-Up
- Frame checksum mismatches persist in `wal-browser` output (e.g.,
`00000000-00000000 != 14d64367-7b77a5a0`). This is a separate issue in
`begin_write_wal_frame` or WAL frame initialization, to be addressed in
a subsequent PR.
Closes: #1137

Closes #1143
This commit is contained in:
Pekka Enberg
2025-03-20 08:46:51 +02:00

View File

@@ -1,4 +1,4 @@
use super::{common, Completion, File, OpenFlags, IO};
use super::{common, Completion, File, OpenFlags, WriteCompletion, IO};
use crate::{LimboError, Result};
use rustix::fs::{self, FlockOperation, OFlags};
use rustix::io_uring::iovec;
@@ -279,7 +279,14 @@ impl File for UringFile {
.build()
.user_data(io.ring.get_key())
};
io.ring.submit_entry(&write, c);
io.ring.submit_entry(
&write,
Completion::Write(WriteCompletion::new(Box::new(move |result| {
c.complete(result);
// NOTE: Explicitly reference buffer to ensure it lives until here
let _ = buffer.borrow();
}))),
);
Ok(())
}