Skip to content

Commit

Permalink
fix cycle detection
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyan-dfinity committed Feb 22, 2024
1 parent 6146965 commit 1fe4a3e
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 44 deletions.
2 changes: 1 addition & 1 deletion rust/bench/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ fn extra_args() -> BenchResult {
let vec_null = hex::decode("4449444c036c01d6fca702016d026c00010080ade204").unwrap();
let vec_opt_record = hex::decode("4449444c176c02017f027f6c02010002006c02000101016c02000201026c02000301036c02000401046c02000501056c02000601066c02000701076c02000801086c02000901096c02000a010a6c02000b010b6c02000c010c6c02000d020d6c02000e010e6c02000f010f6c02001001106c02001101116c02001201126c02001301136e146d150116050101010101").unwrap();
bench_fn(|| {
//assert!(Decode!([COST; SKIP]; &vec_null).is_err());
assert!(Decode!([COST; SKIP]; &vec_null).is_err());
assert!(Decode!([COST; SKIP]; &vec_opt_record).is_err());
})
}
Expand Down
2 changes: 1 addition & 1 deletion rust/candid/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl<'de> IDLDeserialize<'de> {
"Cannot parse header".to_string()
}
})?;
de.add_cost(de.input.position() as usize * 2)?;
de.add_cost(de.input.position() as usize * 4)?;
Ok(IDLDeserialize { de })
}
/// Deserialize one value from deserializer.
Expand Down
76 changes: 38 additions & 38 deletions rust/candid/src/types/type_env.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::types::{Function, Type, TypeInner};
use crate::{Error, Result};
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeMap;

#[derive(Debug, Clone, Default)]
pub struct TypeEnv(pub BTreeMap<String, Type>);
Expand Down Expand Up @@ -80,50 +80,50 @@ impl TypeEnv {
}
Err(Error::msg(format!("cannot find method {id}")))
}
fn go<'a>(
fn is_empty<'a>(
&'a self,
seen: &mut BTreeSet<&'a str>,
res: &mut BTreeSet<&'a str>,
t: &'a Type,
) -> Result<()> {
if !res.is_empty() {
return Ok(());
}
match t.as_ref() {
TypeInner::Record(fs) => {
for f in fs {
self.go(seen, res, &f.ty)?;
}
res: &mut BTreeMap<&'a str, Option<bool>>,
id: &'a str,
) -> Result<bool> {
match res.get(id) {
None => {
res.insert(id, None);
let t = self.find_type(id)?;
let result = match t.as_ref() {
TypeInner::Record(fs) => {
for f in fs {
// Assume env only comes from type table, f.ty is either primitive or var.
if let TypeInner::Var(f_id) = f.ty.as_ref() {
if self.is_empty(res, f_id)? {
res.insert(id, Some(true));
return Ok(true);
}
}
}
false
}
TypeInner::Var(id) => self.is_empty(res, id)?,
_ => false,
};
res.insert(id, Some(result));
Ok(result)
}
TypeInner::Var(id) => {
if seen.insert(id) {
let t = self.find_type(id)?;
self.go(seen, res, t)?;
seen.remove(&id.as_str());
} else {
*res = seen.clone();
}
Some(None) => {
res.insert(id, Some(true));
Ok(true)
}
_ => (),
}
Ok(())
}
fn check_empty(&self) -> Result<BTreeSet<&str>> {
let mut res = BTreeSet::new();
for (name, t) in &self.0 {
let mut seen: BTreeSet<&str> = BTreeSet::new();
let mut local_res = BTreeSet::new();
seen.insert(name);
self.go(&mut seen, &mut local_res, t)?;
res.append(&mut local_res);
Some(Some(b)) => Ok(*b),
}
Ok(res)
}
pub fn replace_empty(&mut self) -> Result<()> {
let ids: Vec<_> = self
.check_empty()?
let mut res = BTreeMap::new();
for name in self.0.keys() {
self.is_empty(&mut res, name)?;
}
let ids: Vec<_> = res
.iter()
.map(|x| (*x).to_string())
.filter(|(_, v)| matches!(v, Some(true)))
.map(|(id, _)| id.to_string())
.collect();
for id in ids {
self.0.insert(id, TypeInner::Empty.into());
Expand Down
13 changes: 9 additions & 4 deletions rust/candid/tests/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ fn test_error() {
|| {
test_decode(b"DIDL\x02\x6c\x01\x0a\x01\x6d\x00\x01\x01 ", &candid::Reserved)
},
// Depending on stack size, we either get recursion limit or parser error
// Depending on stack size, we either reach recursion limit or skipping limit
"Recursion limit exceeded",
"binary parser error",
"Skipping cost exceeds the limit",
);
}

Expand Down Expand Up @@ -534,7 +534,12 @@ fn test_vector() {
let bytes = hex("4449444c036c01d6fca702016d026c00010080ade204");
check_error(
|| test_decode(&bytes, &candid::Reserved),
"Decoding cost exceeds the limit",
"Skipping cost exceeds the limit",
);
let bytes = hex("4449444c176c02017f027f6c02010002006c02000101016c02000201026c02000301036c02000401046c02000501056c02000601066c02000701076c02000801086c02000901096c02000a010a6c02000b010b6c02000c010c6c02000d020d6c02000e010e6c02000f010f6c02001001106c02001101116c02001201126c02001301136e146d150116050101010101");
check_error(
|| test_decode(&bytes, &candid::Reserved),
"Skipping cost exceeds the limit",
);
}

Expand Down Expand Up @@ -775,7 +780,7 @@ where
T: PartialEq + serde::de::Deserialize<'de> + std::fmt::Debug + CandidType,
{
let cost = 20_000_000;
let skip = 10_000;
let skip = 100_000;
let mut config = DecoderConfig::new();
config.set_decoding_quota(cost).set_skipping_quota(skip);
let decoded_one = decode_one_with_config::<T>(bytes, config).unwrap();
Expand Down

0 comments on commit 1fe4a3e

Please sign in to comment.