bitcoindevkit / bitcoindevkit/bdk

file_store: `Store::load` never terminates when a changeset entry decodes to zero bytes

Open Beginner friendly
#2,307 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Rust
Stars
1.1k
Forks
483
Avg merge
20d 3h
Merged PRs (30d)
3

Description

**Describe the bug**

`EntryIter::next` (`crates/file_store/src/entry_iter.rs`) treats every successful `deserialize_from` as progress but never checks that the read advanced the file offset. For a changeset type whose bincode encoding is zero bytes (e.g. `()`, which implements `Merge` in `bdk_core`), any trailing byte after the magic bytes makes `Store::dump` decode the same position over and over, so `Store::load` never returns.

This affects loading a corrupted or hand-edited store file. #2258 appears to address this by framing each entry with a length prefix.

This issue was found by AI.

**To Reproduce**

Add `crates/file_store/tests/test_zero_width.rs` and run `cargo test -p bdk_file_store --test test_zero_width`:

```rust
use bdk_file_store::Store;
use std::time::Duration;

const MAGIC: &[u8] = b"bdk_test_magic";

#[test]
fn load_terminates_on_zero_width_changeset() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("db");
let mut bytes = MAGIC.to_vec();
bytes.push(0xff); // any trailing byte
std::fs::write(&path, &bytes).unwrap();

let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let result = Store::<()>::load(MAGIC, &path);
let _ = tx.send(result.is_ok());
});
// `()` decodes as zero bytes, so the file offset never advances and `load` never reaches EOF.
assert!(
rx.recv_timeout(Duration::from_secs(5)).is_ok(),
"load did not terminate"
);
}
```

Current output:

```
thread 'load_terminates_on_zero_width_changeset' panicked at crates/file_store/tests/test_zero_width.rs:20:5:
load did not terminate
```

**Expected behavior**

Loading a store file should always terminate, returning either the aggregated changeset or an error.

Contributor guide

Open the contributing guide

Research direction

Start in crates/file_store/src/entry_iter.rs, especially EntryIter::next and its deserialization loop. Add or use crates/file_store/tests/test_zero_width.rs, then run cargo test -p bdk_file_store --test test_zero_width. Done means Store::load terminates for a zero-width changeset followed by a trailing byte and returns either the changeset or an error.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
databases
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.