Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ trace-*.json
loom_test.json
.env
sponsorkit/.cache.json
.claude/
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions crates/fuzz/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ path = "fuzz_targets/all.rs"
test = false
doc = false

[[bin]]
name = "diff_calc"
path = "fuzz_targets/diff_calc.rs"
test = false
doc = false

[[bin]]
name = "gc_fuzz"
path = "fuzz_targets/gc_fuzz.rs"
Expand Down
152 changes: 152 additions & 0 deletions crates/fuzz/fuzz/fuzz_targets/diff_calc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#![no_main]

use fuzz::{
actions::ActionWrapper, test_multi_sites,
test_multi_sites_on_one_doc_with_peer_seed_and_targets, Action, FuzzTarget,
};
use libfuzzer_sys::fuzz_target;
use loro::ContainerType;

fn lca_biased_actions(actions: Vec<Action>) -> Vec<Action> {
let mut biased = Vec::with_capacity(actions.len().saturating_mul(2).min(128));
for (i, action) in actions.into_iter().take(48).enumerate() {
biased.push(action);

let site = ((i * 37) % 251) as u8;
let other = site.wrapping_add(1);
let version = (i as u32).wrapping_mul(97);
let injected = match i % 8 {
0 => Action::Sync {
from: site,
to: other,
},
1 => Action::DiffApply {
from: site,
to: other,
},
2 => Action::Checkout { site, to: version },
3 => Action::ForkAt { site, to: version },
4 => Action::ImportShallow { site, from: other },
5 => Action::ExportShallow { site },
6 => Action::StateOnlyRoundTrip { site },
_ => Action::Commit { site },
};
biased.push(injected);
}

biased
}

fn run_text_diff_calc(actions: Vec<Action>) {
let mut actions = lca_biased_actions(actions);
test_multi_sites(5, vec![FuzzTarget::Text], &mut actions);
}

fn run_one_doc_diff_calc(actions: Vec<Action>) {
let peer_seed = peer_seed_from_actions(&actions);
let mut actions = lca_biased_actions(actions);
test_multi_sites_on_one_doc_with_peer_seed_and_targets(
5,
peer_seed,
vec![ContainerType::Text],
&mut actions,
);
}

fn mix_seed(seed: u64, value: u64) -> u64 {
seed ^ value
.wrapping_add(0x9E37_79B9_7F4A_7C15)
.wrapping_add(seed << 6)
.wrapping_add(seed >> 2)
}

fn peer_seed_from_actions(actions: &[Action]) -> u64 {
let mut seed = mix_seed(0xD1FF_CA1C_7E57_0001, actions.len() as u64);
for action in actions.iter().take(8) {
seed = match action {
Action::Handle {
site,
target,
container,
action,
} => {
let mut seed = mix_seed(seed, 0);
seed = mix_seed(seed, *site as u64);
seed = mix_seed(seed, *target as u64);
seed = mix_seed(seed, *container as u64);
if let ActionWrapper::Generic(g) = action {
seed = mix_seed(seed, g.bool as u64);
seed = mix_seed(seed, g.key as u64);
seed = mix_seed(seed, g.pos as u64);
seed = mix_seed(seed, g.length as u64);
seed = mix_seed(seed, g.prop);
}
seed
}
Action::Checkout { site, to } => mix_seed(mix_seed(seed, 1), ((*site as u64) << 32) | *to as u64),
Action::Undo { site, op_len } => {
mix_seed(mix_seed(seed, 2), ((*site as u64) << 32) | *op_len as u64)
}
Action::SyncAllUndo { site, op_len } => {
mix_seed(mix_seed(seed, 3), ((*site as u64) << 32) | *op_len as u64)
}
Action::Sync { from, to } => {
mix_seed(mix_seed(seed, 4), ((*from as u64) << 8) | *to as u64)
}
Action::SyncAll => mix_seed(seed, 5),
Action::ForkAt { site, to } => {
mix_seed(mix_seed(seed, 6), ((*site as u64) << 32) | *to as u64)
}
Action::DiffApply { from, to } => {
mix_seed(mix_seed(seed, 7), ((*from as u64) << 8) | *to as u64)
}
Action::Query {
site,
target,
query_type,
} => mix_seed(
mix_seed(seed, 8),
((*site as u64) << 16) | ((*target as u64) << 8) | *query_type as u64,
),
Action::ExportShallow { site } => mix_seed(mix_seed(seed, 9), *site as u64),
Action::ImportShallow { site, from } => {
mix_seed(mix_seed(seed, 10), ((*site as u64) << 8) | *from as u64)
}
Action::StateOnlyRoundTrip { site } => mix_seed(mix_seed(seed, 11), *site as u64),
Action::Commit { site } => mix_seed(mix_seed(seed, 12), *site as u64),
Action::SetCommitOptions { site, origin, msg } => mix_seed(
mix_seed(seed, 13),
((*site as u64) << 16) | ((*origin as u64) << 8) | *msg as u64,
),
};
}
seed
}

fuzz_target!(|actions: Vec<Action>| {
if actions.is_empty() {
return;
}

let use_one_doc = match &actions[0] {
Action::Handle { site, .. }
| Action::Checkout { site, .. }
| Action::Undo { site, .. }
| Action::SyncAllUndo { site, .. }
| Action::ForkAt { site, .. }
| Action::Query { site, .. }
| Action::ExportShallow { site }
| Action::ImportShallow { site, .. }
| Action::StateOnlyRoundTrip { site }
| Action::Commit { site }
| Action::SetCommitOptions { site, .. } => site % 2 == 1,
Action::Sync { from, .. } | Action::DiffApply { from, .. } => from % 2 == 1,
Action::SyncAll => false,
};

if use_one_doc {
run_one_doc_diff_calc(actions);
} else {
run_text_diff_calc(actions);
}
});
2 changes: 1 addition & 1 deletion crates/fuzz/src/container/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl ActorTrait for TextActor {
.unwrap()
.text
.to_delta();
assert_eq!(value, text_h);
assert_eq!(value, text_h, "peer={}", loro.peer_id());
}

fn add_new_container(&mut self, container: Container) {
Expand Down
10 changes: 7 additions & 3 deletions crates/fuzz/src/container/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,9 @@ impl Actionable for TreeAction {
peer: before.0,
counter: before.1,
};
super::unwrap(tree.mov_before(target, before));
if let Err(LoroError::TreeError(e)) = tree.mov_before(target, before) {
tracing::warn!("move before error {}", e);
}
None
}
TreeActionInner::MoveAfter { target, after } => {
Expand All @@ -345,7 +347,9 @@ impl Actionable for TreeAction {
peer: after.0,
counter: after.1,
};
super::unwrap(tree.mov_after(target, after));
if let Err(LoroError::TreeError(e)) = tree.mov_after(target, after) {
tracing::warn!("move after error {}", e);
}
None
}
TreeActionInner::Meta { meta: (k, v) } => {
Expand All @@ -362,7 +366,7 @@ impl Actionable for TreeAction {
}
TreeActionInner::MetaDelete { key } => {
let meta = super::unwrap(tree.get_meta(target))?;
meta.delete(key);
let _ = meta.delete(key);
None
}
TreeActionInner::MetaClear => {
Expand Down
23 changes: 19 additions & 4 deletions crates/fuzz/src/crdt_fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,11 @@ impl CRDTFuzzer {
let a_frontiers = a.loro.oplog_frontiers();
let b_frontiers = b.loro.oplog_frontiers();
if let Ok(diff) = a.loro.diff(&a_frontiers, &b_frontiers) {
let _ = b.loro.apply_diff(diff);
let before_apply = b.loro.state_frontiers();
let result = b.loro.apply_diff(diff);
if result.is_ok() || b.loro.state_frontiers() != before_apply {
b.loro.commit();
}
}
}
Action::Query {
Expand Down Expand Up @@ -427,7 +431,18 @@ impl CRDTFuzzer {
if let Ok(bytes) = actor.loro.export(loro::ExportMode::state_only(Some(&f))) {
let new_doc = LoroDoc::new();
if new_doc.import(&bytes).is_ok() {
assert_eq!(new_doc.get_deep_value(), actor.loro.get_deep_value());
assert_eq!(
new_doc.get_deep_value(),
actor.loro.get_deep_value(),
"site={site} state_frontiers={:?} oplog_frontiers={:?} oplog_vv={:?} imported_frontiers={:?} imported_vv={:?} shallow_frontiers={:?} shallow_vv={:?}",
actor.loro.state_frontiers(),
actor.loro.oplog_frontiers(),
actor.loro.oplog_vv(),
new_doc.oplog_frontiers(),
new_doc.oplog_vv(),
new_doc.shallow_since_frontiers(),
new_doc.shallow_since_vv(),
);
}
}
}
Expand Down Expand Up @@ -463,8 +478,8 @@ impl CRDTFuzzer {
if a_shallow || b_shallow {
continue;
}
let a_doc = &mut a.loro;
let b_doc = &mut b.loro;
let a_doc = &a.loro;
let b_doc = &b.loro;
info_span!("Attach", peer = i).in_scope(|| {
a_doc.attach();
});
Expand Down
5 changes: 4 additions & 1 deletion crates/fuzz/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ pub use mem_kv_fuzzer::{
minify_simple as kv_minify_simple, test_mem_kv_fuzzer, test_random_bytes_import,
Action as KVAction,
};
pub use one_doc_fuzzer::test_multi_sites_on_one_doc;
pub use one_doc_fuzzer::{
test_multi_sites_on_one_doc, test_multi_sites_on_one_doc_with_peer_seed,
test_multi_sites_on_one_doc_with_peer_seed_and_targets,
};
Loading
Loading