Fix crash in `SerializedSignature` formatting
What changed, and why it matters
This commit fixes a crash when printing or formatting a Taproot serialized signature. The old code assumed every signature was the maximum possible length, but real signatures can be shorter. When a shorter signature was formatted, the code could panic or crash. The fix checks the actual length and formats with the correct size.
Upgrade to the fixed commit. If upgrading is not possible, avoid formatting or serializing SerializedSignature values for untrusted inputs, and audit callers that invoke Display/Debug/to_hex on Taproot signatures.
Security signals we found
Denial-of-service vector: formatting a shorter SerializedSignature could panic
Incorrect buffer-size assumption in hex formatting macro
Crash triggered via common traits (Display, LowerHex, UpperHex, Debug, to_hex)
No evidence of memory corruption or code execution
Evidence from the diff
SerializedSignature in crypto/src/taproot.rs wraps a byte array of up to MAX_LEN bytes but stores a runtime length. The previous Display/LowerHex/UpperHex implementations used fmt_hex_exact! with MAX_LEN unconditionally, which requires an exact-size buffer. For signatures shorter than MAX_LEN this caused a panic/crash. The patch adds an internal fmt_internal helper that chooses MAX_LEN or MAX_LEN-1 based on whether the signature is the default (full-length) variant, and routes Display through LowerHex. The to_hex_from_lower_hex macro also now uses signature.len() for capacity.
Changed components
crypto/src/taproot.rsSerializedSignaturefmt::Display / fmt::LowerHex / fmt::UpperHex / fmt::Debug implementationsimpl_to_hex_from_lower_hex! macro usageInspect captured patch +16 / −4
diff --git a/crypto/src/taproot.rs b/crypto/src/taproot.rs
index 9ed47d87..6bf7deb3 100644
--- a/crypto/src/taproot.rs
+++ b/crypto/src/taproot.rs
@@ -12,7 +12,6 @@ use core::str::FromStr;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
-use hex_unstable::DisplayHex as _;
use internals::array::ArrayExt;
use internals::impl_to_hex_from_lower_hex;
#[cfg(feature = "serde")]
@@ -157,6 +156,19 @@ impl SerializedSignature {
#[inline]
pub fn iter(&self) -> core::slice::Iter<'_, u8> { self.into_iter() }
+ fn is_default(&self) -> bool {
+ self.len() != MAX_LEN
+ }
+
+ #[inline]
+ fn fmt_internal(&self, f: &mut fmt::Formatter, case: hex_unstable::Case) -> fmt::Result {
+ if self.is_default() {
+ hex_unstable::fmt_hex_exact!(f, MAX_LEN - 1, self, case)
+ } else {
+ hex_unstable::fmt_hex_exact!(f, MAX_LEN, self, case)
+ }
+ }
+
/// Constructs new `SerializedSignature` from data and length.
///
/// # Panics
@@ -179,14 +191,14 @@ impl fmt::Debug for SerializedSignature {
impl fmt::Display for SerializedSignature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- hex_unstable::fmt_hex_exact!(f, MAX_LEN, self, hex_unstable::Case::Lower)
+ fmt::LowerHex::fmt(self, f)
}
}
impl fmt::LowerHex for SerializedSignature {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::LowerHex::fmt(&(**self).as_hex(), f)
+ self.fmt_internal(f, hex_unstable::Case::Lower)
}
}
impl_to_hex_from_lower_hex!(SerializedSignature, |signature: &SerializedSignature| signature.len
@@ -195,7 +207,7 @@ impl_to_hex_from_lower_hex!(SerializedSignature, |signature: &SerializedSignatur
impl fmt::UpperHex for SerializedSignature {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::UpperHex::fmt(&(**self).as_hex(), f)
+ self.fmt_internal(f, hex_unstable::Case::Upper)
}
}
Why this scored 44/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.