Skip to content

Commit a396605

Browse files
committed
Fix taproot internal key parsing
Caught by fuzz tests. We directly assumed that the internal key is a valid string if it was between '(' and ')' and did not contain any ','
1 parent 68c7c49 commit a396605

File tree

2 files changed

+42
-18
lines changed

2 files changed

+42
-18
lines changed

fuzz/fuzz_targets/roundtrip_descriptor.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,27 @@ fn main() {
2323

2424
#[cfg(test)]
2525
mod tests {
26-
use super::*;
26+
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
27+
let mut b = 0;
28+
for (idx, c) in hex.as_bytes().iter().enumerate() {
29+
b <<= 4;
30+
match *c {
31+
b'A'..=b'F' => b |= c - b'A' + 10,
32+
b'a'..=b'f' => b |= c - b'a' + 10,
33+
b'0'..=b'9' => b |= c - b'0',
34+
_ => panic!("Bad hex"),
35+
}
36+
if (idx & 1) == 1 {
37+
out.push(b);
38+
b = 0;
39+
}
40+
}
41+
}
42+
2743
#[test]
28-
fn test() {
29-
do_test(b"pkh()");
44+
fn duplicate_crash3() {
45+
let mut a = Vec::new();
46+
extend_vec_from_hex("747228726970656d616e645f6e5b5c79647228726970656d616e645f6e5b5c7964646464646464646464646464646464646464646464646464646b5f6872702c29", &mut a);
47+
super::do_test(&a);
3048
}
3149
}

src/descriptor/tr.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,14 @@ fn parse_tr_tree(s: &str) -> Result<expression::Tree, Error> {
527527
if s.len() > 3 && &s[..3] == "tr(" && s.as_bytes()[s.len() - 1] == b')' {
528528
let rest = &s[3..s.len() - 1];
529529
if !rest.contains(',') {
530+
let key = expression::Tree::from_str(rest)?;
531+
if !key.args.is_empty() {
532+
return Err(Error::Unexpected(
533+
"invalid taproot internal key".to_string(),
534+
));
535+
}
530536
let internal_key = expression::Tree {
531-
name: rest,
537+
name: key.name,
532538
args: vec![],
533539
};
534540
return Ok(expression::Tree {
@@ -540,8 +546,14 @@ fn parse_tr_tree(s: &str) -> Result<expression::Tree, Error> {
540546
let (key, script) = split_once(rest, ',')
541547
.ok_or_else(|| Error::BadDescriptor("invalid taproot descriptor".to_string()))?;
542548

549+
let key = expression::Tree::from_str(key)?;
550+
if !key.args.is_empty() {
551+
return Err(Error::Unexpected(
552+
"invalid taproot internal key".to_string(),
553+
));
554+
}
543555
let internal_key = expression::Tree {
544-
name: key,
556+
name: key.name,
545557
args: vec![],
546558
};
547559
if script.is_empty() {
@@ -568,19 +580,13 @@ fn split_once(inp: &str, delim: char) -> Option<(&str, &str)> {
568580
if inp.is_empty() {
569581
None
570582
} else {
571-
let mut found = inp.len();
572-
for (idx, ch) in inp.chars().enumerate() {
573-
if ch == delim {
574-
found = idx;
575-
break;
576-
}
577-
}
578-
// No comma or trailing comma found
579-
if found >= inp.len() - 1 {
580-
Some((inp, ""))
581-
} else {
582-
Some((&inp[..found], &inp[found + 1..]))
583-
}
583+
// find the first character that matches delim
584+
let res = inp
585+
.chars()
586+
.position(|ch| ch == delim)
587+
.map(|idx| (&inp[..idx], &inp[idx + 1..]))
588+
.unwrap_or((inp, ""));
589+
Some(res)
584590
}
585591
}
586592

0 commit comments

Comments
 (0)