-
Notifications
You must be signed in to change notification settings - Fork 179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Raising an error upon failure to deserialize overflowing number #529
Conversation
value: Decimal, | ||
} | ||
// Going a fraction about the MAX number expecting it to fail but not panic. | ||
let res: Result<Decimal, _> = Decimal::from_str(format!("{}.999999", Decimal::MAX).as_str()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of catching the expected deserialization error, it panicked like this:
thread 'serde::test::with_overflow' panicked at 'assertion failed: `(left == right)`
left: `1`,
right: `0`', src/str.rs:384:5
I would prefer being able to throw any string at Decimal::str rather than implementing a separate validation layer.
Since I'm expecting you to say that the use of // Calculated from Decimal::MAX display length
const MAX_DECIMAL_LEN: usize = 29;
lazy_static! {
static ref DECIMAL_RE: Regex =
Regex::new(r"[+-]?((?P<whole>\d+)?(?P<dot>[.]))?(?P<decimal>\d+)")
.expect("Invalid decimal validation pattern");
}
impl FromStr for DecimalWrapper {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let cap = DECIMAL_RE
.captures(s)
.ok_or(anyhow!("Invalid decimal s={}", s))?;
// Run basic heuristics on the regex captures before attempting any parsing.
let whole;
let fract;
// Case: 1234.4444
if let Some(whole_) = cap.name("whole") {
whole = whole_.as_str();
fract = &cap["decimal"];
// Case: .4444
} else if let Some(dot) = cap.name("dot") {
whole = "";
fract = &cap["decimal"];
// Case: 1234
} else {
whole = &cap["decimal"];
fract = "";
}
ensure!(
fract.len() <= DEFAULT_CURRENCY_DECIMAL_PRECISION as usize,
"Too many numbers after the decimal len({}) > {}",
fract,
DEFAULT_CURRENCY_DECIMAL_PRECISION
);
// Effectively reducing the Decimal capacity, which is fine here since our internal threshold is lower.
ensure!(
whole.len() < MAX_DECIMAL_LEN,
"Preventing Decimal overflow len({}) > {}",
whole,
MAX_DECIMAL_LEN
);
Ok(DecimalWrapper::new(Decimal::from_str(s)?))
}
} |
Thanks for your contribution! To describe previous functionality, the test: let res: Result<Decimal, _> = Decimal::from_str(format!("{}.999999", Decimal::MAX).as_str()); would previously return Regarding a fix: this is an interesting one - in theory it should be throwing an error already with the By the looks of it, what is happening is that this code is hitting While I think this bug is legit, I'd perhaps fix this in a slightly different way to limit additional checks within |
Closing due to fix in #530 |
I found this panic scenario with the Decimal::serde deserializer when fuzz testing.