Skip to content

Commit 8f54b5e

Browse files
committed
Merge #712: Fix panic while decoding large Miniscripts from Script
192c499 Release 12.2 (Sanket Kanjalkar) 702981a Fix panic while decoding large Miniscripts from Script (Sanket Kanjalkar) Pull request description: Use private module to guard all constructors. ACKs for top commit: apoelstra: ACK 192c499 successfully ran local tests Tree-SHA512: d2a08c34062f8c33b5e6a2f50f93df957a56812933c1424e47297e05e19164b25483b6ceb861eab333f22f33c5b142d84b8ab031e72d679d7adfa3cf05da6e10
2 parents aec747e + 192c499 commit 8f54b5e

File tree

6 files changed

+100
-108
lines changed

6 files changed

+100
-108
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# # 12.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)
4+
15
# 12.1.0 - July 9, 2024
26

37
- Make `LoggerAssetProvider` constructible [#697](https://github.com/rust-bitcoin/rust-miniscript/pull/697)

Cargo-minimal.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ dependencies = [
316316

317317
[[package]]
318318
name = "miniscript"
319-
version = "12.1.0"
319+
version = "12.2.0"
320320
dependencies = [
321321
"bech32",
322322
"bitcoin",

Cargo-recent.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ dependencies = [
294294

295295
[[package]]
296296
name = "miniscript"
297-
version = "12.1.0"
297+
version = "12.2.0"
298298
dependencies = [
299299
"bech32",
300300
"bitcoin",

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "miniscript"
3-
version = "12.1.0"
3+
version = "12.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

Lines changed: 2 additions & 12 deletions
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

@@ -15,8 +14,6 @@ use sync::Arc;
1514

1615
use crate::miniscript::lex::{Token as Tk, TokenIter};
1716
use crate::miniscript::limits::{MAX_PUBKEYS_IN_CHECKSIGADD, MAX_PUBKEYS_PER_MULTISIG};
18-
use crate::miniscript::types::extra_props::ExtData;
19-
use crate::miniscript::types::Type;
2017
use crate::miniscript::ScriptContext;
2118
use crate::prelude::*;
2219
#[cfg(doc)]
@@ -213,10 +210,7 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> TerminalStack<Pk, Ctx> {
213210

214211
///reduce, type check and push a 0-arg node
215212
fn reduce0(&mut self, ms: Terminal<Pk, Ctx>) -> Result<(), Error> {
216-
let ty = Type::type_check(&ms)?;
217-
let ext = ExtData::type_check(&ms)?;
218-
let ms = Miniscript { node: ms, ty, ext, phantom: PhantomData };
219-
Ctx::check_global_validity(&ms)?;
213+
let ms = Miniscript::from_ast(ms)?;
220214
self.0.push(ms);
221215
Ok(())
222216
}
@@ -524,11 +518,7 @@ pub fn parse<Ctx: ScriptContext>(
524518
let c = term.pop().unwrap();
525519
let wrapped_ms = Terminal::AndOr(Arc::new(a), Arc::new(c), Arc::new(b));
526520

527-
let ty = Type::type_check(&wrapped_ms)?;
528-
let ext = ExtData::type_check(&wrapped_ms)?;
529-
530-
term.0
531-
.push(Miniscript { node: wrapped_ms, ty, ext, phantom: PhantomData });
521+
term.0.push(Miniscript::from_ast(wrapped_ms)?);
532522
}
533523
Some(NonTerm::ThreshW { n, k }) => {
534524
match_token!(

src/miniscript/mod.rs

Lines changed: 91 additions & 93 deletions
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;
@@ -23,7 +22,7 @@ use self::analyzable::ExtParams;
2322
pub use self::context::{BareCtx, Legacy, Segwitv0, Tap};
2423
use crate::iter::TreeLike;
2524
use crate::prelude::*;
26-
use crate::{script_num_size, TranslateErr, MAX_RECURSION_DEPTH};
25+
use crate::{script_num_size, TranslateErr};
2726

2827
pub mod analyzable;
2928
pub mod astelem;
@@ -42,79 +41,89 @@ use sync::Arc;
4241
use self::lex::{lex, TokenIter};
4342
pub use crate::miniscript::context::ScriptContext;
4443
use crate::miniscript::decode::Terminal;
45-
use crate::miniscript::types::extra_props::ExtData;
46-
use crate::miniscript::types::Type;
4744
use crate::{
4845
expression, plan, Error, ForEachKey, FromStrKey, MiniscriptKey, ToPublicKey, TranslatePk,
4946
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-
/// The `1` combinator.
69-
pub const TRUE: Self = Miniscript {
70-
node: Terminal::True,
71-
ty: types::Type::TRUE,
72-
ext: types::extra_props::ExtData::TRUE,
73-
phantom: PhantomData,
74-
};
75-
76-
/// The `0` combinator.
77-
pub const FALSE: Self = Miniscript {
78-
node: Terminal::False,
79-
ty: types::Type::FALSE,
80-
ext: types::extra_props::ExtData::FALSE,
81-
phantom: PhantomData,
82-
};
83-
84-
/// Add type information(Type and Extdata) to Miniscript based on
85-
/// `AstElem` fragment. Dependent on display and clone because of Error
86-
/// Display code of type_check.
87-
pub fn from_ast(t: Terminal<Pk, Ctx>) -> Result<Miniscript<Pk, Ctx>, Error> {
88-
let res = Miniscript {
89-
ty: Type::type_check(&t)?,
90-
ext: ExtData::type_check(&t)?,
91-
node: t,
54+
use super::types::{ExtData, 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>,
70+
}
71+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
72+
/// The `1` combinator.
73+
pub const TRUE: Self = Miniscript {
74+
node: Terminal::True,
75+
ty: types::Type::TRUE,
76+
ext: types::extra_props::ExtData::TRUE,
9277
phantom: PhantomData,
9378
};
94-
// TODO: This recursion depth is based on segwitv0.
95-
// We can relax this in tapscript, but this should be good for almost
96-
// all practical cases and we can revisit this if needed.
97-
// casting to u32 is safe because tree_height will never go more than u32::MAX
98-
if (res.ext.tree_height as u32) > MAX_RECURSION_DEPTH {
99-
return Err(Error::MaxRecursiveDepthExceeded);
79+
80+
/// The `0` combinator.
81+
pub const FALSE: Self = Miniscript {
82+
node: Terminal::False,
83+
ty: types::Type::FALSE,
84+
ext: types::extra_props::ExtData::FALSE,
85+
phantom: PhantomData,
86+
};
87+
88+
/// Add type information(Type and Extdata) to Miniscript based on
89+
/// `AstElem` fragment. Dependent on display and clone because of Error
90+
/// Display code of type_check.
91+
pub fn from_ast(t: Terminal<Pk, Ctx>) -> Result<Miniscript<Pk, Ctx>, Error> {
92+
let res = Miniscript {
93+
ty: Type::type_check(&t)?,
94+
ext: ExtData::type_check(&t)?,
95+
node: t,
96+
phantom: PhantomData,
97+
};
98+
// TODO: This recursion depth is based on segwitv0.
99+
// We can relax this in tapscript, but this should be good for almost
100+
// all practical cases and we can revisit this if needed.
101+
// casting to u32 is safe because tree_height will never go more than u32::MAX
102+
if (res.ext.tree_height as u32) > MAX_RECURSION_DEPTH {
103+
return Err(Error::MaxRecursiveDepthExceeded);
104+
}
105+
Ctx::check_global_consensus_validity(&res)?;
106+
Ok(res)
100107
}
101-
Ctx::check_global_consensus_validity(&res)?;
102-
Ok(res)
103-
}
104108

105-
/// Create a new `Miniscript` from a `Terminal` node and a `Type` annotation
106-
/// This does not check the typing rules. The user is responsible for ensuring
107-
/// that the type provided is correct.
108-
///
109-
/// You should almost always use `Miniscript::from_ast` instead of this function.
110-
pub fn from_components_unchecked(
111-
node: Terminal<Pk, Ctx>,
112-
ty: types::Type,
113-
ext: types::extra_props::ExtData,
114-
) -> Miniscript<Pk, Ctx> {
115-
Miniscript { node, ty, ext, phantom: PhantomData }
109+
/// Create a new `Miniscript` from a `Terminal` node and a `Type` annotation
110+
/// This does not check the typing rules. The user is responsible for ensuring
111+
/// that the type provided is correct.
112+
///
113+
/// You should almost always use `Miniscript::from_ast` instead of this function.
114+
pub fn from_components_unchecked(
115+
node: Terminal<Pk, Ctx>,
116+
ty: types::Type,
117+
ext: types::extra_props::ExtData,
118+
) -> Miniscript<Pk, Ctx> {
119+
Miniscript { node, ty, ext, phantom: PhantomData }
120+
}
116121
}
122+
}
117123

124+
pub use private::Miniscript;
125+
126+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
118127
/// Extracts the `AstElem` representing the root of the miniscript
119128
pub fn into_inner(self) -> Terminal<Pk, Ctx> { self.node }
120129

@@ -700,7 +709,6 @@ pub mod hash256 {
700709
#[cfg(test)]
701710
mod tests {
702711

703-
use core::marker::PhantomData;
704712
use core::str;
705713
use core::str::FromStr;
706714

@@ -710,8 +718,7 @@ mod tests {
710718
use sync::Arc;
711719

712720
use super::{Miniscript, ScriptContext, Segwitv0, Tap};
713-
use crate::miniscript::types::{self, ExtData, Type};
714-
use crate::miniscript::Terminal;
721+
use crate::miniscript::{types, Terminal};
715722
use crate::policy::Liftable;
716723
use crate::prelude::*;
717724
use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator};
@@ -896,21 +903,15 @@ mod tests {
896903
.unwrap();
897904
let hash = hash160::Hash::from_byte_array([17; 20]);
898905

899-
let pk_node = Terminal::Check(Arc::new(Miniscript {
900-
node: Terminal::PkK(String::from("")),
901-
ty: Type::pk_k(),
902-
ext: types::extra_props::ExtData::pk_k::<Segwitv0>(),
903-
phantom: PhantomData,
904-
}));
906+
let pk_node = Terminal::Check(Arc::new(
907+
Miniscript::from_ast(Terminal::PkK(String::from(""))).unwrap(),
908+
));
905909
let pkk_ms: Miniscript<String, Segwitv0> = Miniscript::from_ast(pk_node).unwrap();
906910
dummy_string_rtt(pkk_ms, "[B/onduesm]pk(\"\")", "pk()");
907911

908-
let pkh_node = Terminal::Check(Arc::new(Miniscript {
909-
node: Terminal::PkH(String::from("")),
910-
ty: Type::pk_h(),
911-
ext: types::extra_props::ExtData::pk_h::<Segwitv0>(),
912-
phantom: PhantomData,
913-
}));
912+
let pkh_node = Terminal::Check(Arc::new(
913+
Miniscript::from_ast(Terminal::PkH(String::from(""))).unwrap(),
914+
));
914915
let pkh_ms: Miniscript<String, Segwitv0> = Miniscript::from_ast(pkh_node).unwrap();
915916

916917
let expected_debug = "[B/nduesm]pkh(\"\")";
@@ -926,12 +927,7 @@ mod tests {
926927
assert_eq!(display, expected);
927928
}
928929

929-
let pkk_node = Terminal::Check(Arc::new(Miniscript {
930-
node: Terminal::PkK(pk),
931-
ty: Type::pk_k(),
932-
ext: types::extra_props::ExtData::pk_k::<Segwitv0>(),
933-
phantom: PhantomData,
934-
}));
930+
let pkk_node = Terminal::Check(Arc::new(Miniscript::from_ast(Terminal::PkK(pk)).unwrap()));
935931
let pkk_ms: Segwitv0Script = Miniscript::from_ast(pkk_node).unwrap();
936932

937933
script_rtt(
@@ -940,17 +936,10 @@ mod tests {
940936
202020202ac",
941937
);
942938

943-
let pkh_ms: Segwitv0Script = Miniscript {
944-
node: Terminal::Check(Arc::new(Miniscript {
945-
node: Terminal::RawPkH(hash),
946-
ty: Type::pk_h(),
947-
ext: types::extra_props::ExtData::pk_h::<Segwitv0>(),
948-
phantom: PhantomData,
949-
})),
950-
ty: Type::cast_check(Type::pk_h()).unwrap(),
951-
ext: ExtData::cast_check(ExtData::pk_h::<Segwitv0>()),
952-
phantom: PhantomData,
953-
};
939+
let pkh_ms: Segwitv0Script = Miniscript::from_ast(Terminal::Check(Arc::new(
940+
Miniscript::from_ast(Terminal::RawPkH(hash)).unwrap(),
941+
)))
942+
.unwrap();
954943

955944
script_rtt(pkh_ms, "76a914111111111111111111111111111111111111111188ac");
956945
}
@@ -1462,6 +1451,15 @@ mod tests {
14621451
Err(Error::MaxRecursiveDepthExceeded)
14631452
);
14641453
}
1454+
1455+
#[test]
1456+
fn test_script_parse_dos() {
1457+
let mut script = bitcoin::script::Builder::new().push_opcode(bitcoin::opcodes::OP_TRUE);
1458+
for _ in 0..10000 {
1459+
script = script.push_opcode(bitcoin::opcodes::all::OP_0NOTEQUAL);
1460+
}
1461+
Tapscript::parse_insane(&script.into_script()).unwrap_err();
1462+
}
14651463
}
14661464

14671465
#[cfg(bench)]

0 commit comments

Comments
 (0)