Skip to content

Commit 2c9d2e7

Browse files
committed
Merge #713: Fix panic while decoding large Miniscripts from Script
ec8b8c3 Release 11.2 (Sanket Kanjalkar) 7b656b9 Fix panic while decoding large Miniscripts from Script (Sanket Kanjalkar) Pull request description: Backport 11.x ACKs for top commit: apoelstra: ACK ec8b8c3 Tree-SHA512: d03620f971f8f23cc48816568b557f6de4f862dd254b1d6e494f15e9e42cb0004f81baeca44d0dbd49384860bd2f6b75402b61bd26d299df3ac400f9b81e5eaf
2 parents 2f1a262 + ec8b8c3 commit 2c9d2e7

File tree

4 files changed

+86
-99
lines changed

4 files changed

+86
-99
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# 11.2.0 - July 20, 2024
2+
3+
- Fix panics while decoding large miniscripts from script [#712](https://github.com/rust-bitcoin/rust-miniscript/pull/712)
14

25
# 11.1.0 - July 9, 2024
36

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "miniscript"
3-
version = "11.1.0"
3+
version = "11.2.0"
44
authors = ["Andrew Poelstra <[email protected]>, Sanket Kanjalkar <[email protected]>"]
55
license = "CC0-1.0"
66
homepage = "https://github.com/rust-bitcoin/rust-miniscript/"

src/miniscript/decode.rs

+4-20
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//!
77
88
use core::fmt;
9-
use core::marker::PhantomData;
109
#[cfg(feature = "std")]
1110
use std::error;
1211

@@ -16,8 +15,6 @@ use sync::Arc;
1615

1716
use crate::miniscript::lex::{Token as Tk, TokenIter};
1817
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
19-
use crate::miniscript::types::extra_props::ExtData;
20-
use crate::miniscript::types::{Property, Type};
2118
use crate::miniscript::ScriptContext;
2219
use crate::prelude::*;
2320
#[cfg(doc)]
@@ -212,10 +209,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> TerminalStack<Pk, Ctx> {
212209

213210
///reduce, type check and push a 0-arg node
214211
fn reduce0(&mut self, ms: Terminal<Pk, Ctx>) -> Result<(), Error> {
215-
let ty = Type::type_check(&ms)?;
216-
let ext = ExtData::type_check(&ms)?;
217-
let ms = Miniscript { node: ms, ty, ext, phantom: PhantomData };
218-
Ctx::check_global_validity(&ms)?;
212+
let ms = Miniscript::from_ast(ms)?;
219213
self.0.push(ms);
220214
Ok(())
221215
}
@@ -228,10 +222,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> TerminalStack<Pk, Ctx> {
228222
let top = self.pop().unwrap();
229223
let wrapped_ms = wrap(Arc::new(top));
230224

231-
let ty = Type::type_check(&wrapped_ms)?;
232-
let ext = ExtData::type_check(&wrapped_ms)?;
233-
let ms = Miniscript { node: wrapped_ms, ty, ext, phantom: PhantomData };
234-
Ctx::check_global_validity(&ms)?;
225+
let ms = Miniscript::from_ast(wrapped_ms)?;
235226
self.0.push(ms);
236227
Ok(())
237228
}
@@ -246,10 +237,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> TerminalStack<Pk, Ctx> {
246237

247238
let wrapped_ms = wrap(Arc::new(left), Arc::new(right));
248239

249-
let ty = Type::type_check(&wrapped_ms)?;
250-
let ext = ExtData::type_check(&wrapped_ms)?;
251-
let ms = Miniscript { node: wrapped_ms, ty, ext, phantom: PhantomData };
252-
Ctx::check_global_validity(&ms)?;
240+
let ms = Miniscript::from_ast(wrapped_ms)?;
253241
self.0.push(ms);
254242
Ok(())
255243
}
@@ -531,11 +519,7 @@ pub fn parse<Ctx: ScriptContext>(
531519
let c = term.pop().unwrap();
532520
let wrapped_ms = Terminal::AndOr(Arc::new(a), Arc::new(c), Arc::new(b));
533521

534-
let ty = Type::type_check(&wrapped_ms)?;
535-
let ext = ExtData::type_check(&wrapped_ms)?;
536-
537-
term.0
538-
.push(Miniscript { node: wrapped_ms, ty, ext, phantom: PhantomData });
522+
term.0.push(Miniscript::from_ast(wrapped_ms)?);
539523
}
540524
Some(NonTerm::ThreshW { n, k }) => {
541525
match_token!(

src/miniscript/mod.rs

+78-78
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
//! components of the AST.
1313
//!
1414
15-
use core::marker::PhantomData;
1615
use core::{fmt, hash, str};
1716

1817
use bitcoin::hashes::hash160;
@@ -22,7 +21,7 @@ use bitcoin::taproot::{LeafVersion, TapLeafHash};
2221
use self::analyzable::ExtParams;
2322
pub use self::context::{BareCtx, Legacy, Segwitv0, Tap};
2423
use crate::iter::TreeLike;
25-
use crate::{prelude::*, MAX_RECURSION_DEPTH};
24+
use crate::prelude::*;
2625
use crate::{script_num_size, TranslateErr};
2726

2827
pub mod analyzable;
@@ -43,62 +42,73 @@ use self::lex::{lex, TokenIter};
4342
use self::types::Property;
4443
pub use crate::miniscript::context::ScriptContext;
4544
use crate::miniscript::decode::Terminal;
46-
use crate::miniscript::types::extra_props::ExtData;
47-
use crate::miniscript::types::Type;
4845
use crate::{
4946
expression, plan, Error, ForEachKey, MiniscriptKey, ToPublicKey, TranslatePk, Translator,
5047
};
5148
#[cfg(test)]
5249
mod ms_tests;
5350

54-
/// The top-level miniscript abstract syntax tree (AST).
55-
#[derive(Clone)]
56-
pub struct Miniscript<Pk: MiniscriptKey, Ctx: ScriptContext> {
57-
/// A node in the AST.
58-
pub node: Terminal<Pk, Ctx>,
59-
/// The correctness and malleability type information for the AST node.
60-
pub ty: types::Type,
61-
/// Additional information helpful for extra analysis.
62-
pub ext: types::extra_props::ExtData,
63-
/// Context PhantomData. Only accessible inside this crate
64-
phantom: PhantomData<Ctx>,
65-
}
51+
mod private {
52+
use core::marker::PhantomData;
6653

67-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
68-
/// Add type information(Type and Extdata) to Miniscript based on
69-
/// `AstElem` fragment. Dependent on display and clone because of Error
70-
/// Display code of type_check.
71-
pub fn from_ast(t: Terminal<Pk, Ctx>) -> Result<Miniscript<Pk, Ctx>, Error> {
72-
let res = Miniscript {
73-
ty: Type::type_check(&t)?,
74-
ext: ExtData::type_check(&t)?,
75-
node: t,
76-
phantom: PhantomData,
77-
};
78-
// TODO: This recursion depth is based on segwitv0.
79-
// We can relax this in tapscript, but this should be good for almost
80-
// all practical cases and we can revisit this if needed.
81-
// casting to u32 is safe because tree_height will never go more than u32::MAX
82-
if (res.ext.tree_height as u32) > MAX_RECURSION_DEPTH {
83-
return Err(Error::MaxRecursiveDepthExceeded);
84-
}
85-
Ctx::check_global_consensus_validity(&res)?;
86-
Ok(res)
54+
use super::types::{ExtData, Property, Type};
55+
pub use crate::miniscript::context::ScriptContext;
56+
use crate::miniscript::types;
57+
use crate::{Error, MiniscriptKey, Terminal, MAX_RECURSION_DEPTH};
58+
59+
/// The top-level miniscript abstract syntax tree (AST).
60+
#[derive(Clone)]
61+
pub struct Miniscript<Pk: MiniscriptKey, Ctx: ScriptContext> {
62+
/// A node in the AST.
63+
pub node: Terminal<Pk, Ctx>,
64+
/// The correctness and malleability type information for the AST node.
65+
pub ty: types::Type,
66+
/// Additional information helpful for extra analysis.
67+
pub ext: types::extra_props::ExtData,
68+
/// Context PhantomData. Only accessible inside this crate
69+
phantom: PhantomData<Ctx>,
8770
}
71+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
72+
73+
/// Add type information(Type and Extdata) to Miniscript based on
74+
/// `AstElem` fragment. Dependent on display and clone because of Error
75+
/// Display code of type_check.
76+
pub fn from_ast(t: Terminal<Pk, Ctx>) -> Result<Miniscript<Pk, Ctx>, Error> {
77+
let res = Miniscript {
78+
ty: Type::type_check(&t)?,
79+
ext: ExtData::type_check(&t)?,
80+
node: t,
81+
phantom: PhantomData,
82+
};
83+
// TODO: This recursion depth is based on segwitv0.
84+
// We can relax this in tapscript, but this should be good for almost
85+
// all practical cases and we can revisit this if needed.
86+
// casting to u32 is safe because tree_height will never go more than u32::MAX
87+
if (res.ext.tree_height as u32) > MAX_RECURSION_DEPTH {
88+
return Err(Error::MaxRecursiveDepthExceeded);
89+
}
90+
Ctx::check_global_consensus_validity(&res)?;
91+
Ok(res)
92+
}
8893

89-
/// Create a new `Miniscript` from a `Terminal` node and a `Type` annotation
90-
/// This does not check the typing rules. The user is responsible for ensuring
91-
/// that the type provided is correct.
92-
///
93-
/// You should almost always use `Miniscript::from_ast` instead of this function.
94-
pub fn from_components_unchecked(
95-
node: Terminal<Pk, Ctx>,
96-
ty: types::Type,
97-
ext: types::extra_props::ExtData,
98-
) -> Miniscript<Pk, Ctx> {
99-
Miniscript { node, ty, ext, phantom: PhantomData }
94+
/// Create a new `Miniscript` from a `Terminal` node and a `Type` annotation
95+
/// This does not check the typing rules. The user is responsible for ensuring
96+
/// that the type provided is correct.
97+
///
98+
/// You should almost always use `Miniscript::from_ast` instead of this function.
99+
pub fn from_components_unchecked(
100+
node: Terminal<Pk, Ctx>,
101+
ty: types::Type,
102+
ext: types::extra_props::ExtData,
103+
) -> Miniscript<Pk, Ctx> {
104+
Miniscript { node, ty, ext, phantom: PhantomData }
105+
}
100106
}
107+
}
108+
109+
pub use private::Miniscript;
101110

111+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
102112
/// Extracts the `AstElem` representing the root of the miniscript
103113
pub fn into_inner(self) -> Terminal<Pk, Ctx> { self.node }
104114

@@ -603,7 +613,6 @@ pub mod hash256 {
603613
#[cfg(test)]
604614
mod tests {
605615

606-
use core::marker::PhantomData;
607616
use core::str;
608617
use core::str::FromStr;
609618

@@ -614,7 +623,7 @@ mod tests {
614623
use sync::Arc;
615624

616625
use super::{Miniscript, ScriptContext, Segwitv0, Tap};
617-
use crate::miniscript::types::{self, ExtData, Property, Type};
626+
use crate::miniscript::types;
618627
use crate::miniscript::Terminal;
619628
use crate::policy::Liftable;
620629
use crate::{prelude::*, Error};
@@ -800,21 +809,15 @@ mod tests {
800809
.unwrap();
801810
let hash = hash160::Hash::from_byte_array([17; 20]);
802811

803-
let pk_node = Terminal::Check(Arc::new(Miniscript {
804-
node: Terminal::PkK(String::from("")),
805-
ty: Type::from_pk_k::<Segwitv0>(),
806-
ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
807-
phantom: PhantomData,
808-
}));
812+
let pk_node = Terminal::Check(Arc::new(
813+
Miniscript::from_ast(Terminal::PkK(String::from(""))).unwrap(),
814+
));
809815
let pkk_ms: Miniscript<String, Segwitv0> = Miniscript::from_ast(pk_node).unwrap();
810816
dummy_string_rtt(pkk_ms, "[B/onduesm]c:[K/onduesm]pk_k(\"\")", "pk()");
811817

812-
let pkh_node = Terminal::Check(Arc::new(Miniscript {
813-
node: Terminal::PkH(String::from("")),
814-
ty: Type::from_pk_h::<Segwitv0>(),
815-
ext: types::extra_props::ExtData::from_pk_h::<Segwitv0>(),
816-
phantom: PhantomData,
817-
}));
818+
let pkh_node = Terminal::Check(Arc::new(
819+
Miniscript::from_ast(Terminal::PkH(String::from(""))).unwrap(),
820+
));
818821
let pkh_ms: Miniscript<String, Segwitv0> = Miniscript::from_ast(pkh_node).unwrap();
819822

820823
let expected_debug = "[B/nduesm]c:[K/nduesm]pk_h(\"\")";
@@ -830,12 +833,7 @@ mod tests {
830833
assert_eq!(display, expected);
831834
}
832835

833-
let pkk_node = Terminal::Check(Arc::new(Miniscript {
834-
node: Terminal::PkK(pk),
835-
ty: Type::from_pk_k::<Segwitv0>(),
836-
ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
837-
phantom: PhantomData,
838-
}));
836+
let pkk_node = Terminal::Check(Arc::new(Miniscript::from_ast(Terminal::PkK(pk)).unwrap()));
839837
let pkk_ms: Segwitv0Script = Miniscript::from_ast(pkk_node).unwrap();
840838

841839
script_rtt(
@@ -844,17 +842,10 @@ mod tests {
844842
202020202ac",
845843
);
846844

847-
let pkh_ms: Segwitv0Script = Miniscript {
848-
node: Terminal::Check(Arc::new(Miniscript {
849-
node: Terminal::RawPkH(hash),
850-
ty: Type::from_pk_h::<Segwitv0>(),
851-
ext: types::extra_props::ExtData::from_pk_h::<Segwitv0>(),
852-
phantom: PhantomData,
853-
})),
854-
ty: Type::cast_check(Type::from_pk_h::<Segwitv0>()).unwrap(),
855-
ext: ExtData::cast_check(ExtData::from_pk_h::<Segwitv0>()).unwrap(),
856-
phantom: PhantomData,
857-
};
845+
let pkh_ms: Segwitv0Script = Miniscript::from_ast(Terminal::Check(Arc::new(
846+
Miniscript::from_ast(Terminal::RawPkH(hash)).unwrap(),
847+
)))
848+
.unwrap();
858849

859850
script_rtt(pkh_ms, "76a914111111111111111111111111111111111111111188ac");
860851
}
@@ -1366,4 +1357,13 @@ mod tests {
13661357
Err(Error::MaxRecursiveDepthExceeded)
13671358
);
13681359
}
1360+
1361+
#[test]
1362+
fn test_script_parse_dos() {
1363+
let mut script = bitcoin::script::Builder::new().push_opcode(bitcoin::opcodes::OP_TRUE);
1364+
for _ in 0..10000 {
1365+
script = script.push_opcode(bitcoin::opcodes::all::OP_0NOTEQUAL);
1366+
}
1367+
Tapscript::parse_insane(&script.into_script()).unwrap_err();
1368+
}
13691369
}

0 commit comments

Comments
 (0)