Skip to content

Commit eaf76f6

Browse files
Merge pull request #2969 from TheBlueMatt/2024-03-fix-upgradable-enum
2 parents 1d9e541 + f852d16 commit eaf76f6

File tree

2 files changed

+55
-20
lines changed

2 files changed

+55
-20
lines changed

lightning/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ pub use std::io;
9494
pub use core2::io;
9595

9696
#[cfg(not(feature = "std"))]
97-
mod io_extras {
97+
#[doc(hidden)]
98+
/// IO utilities public only for use by in-crate macros. These should not be used externally
99+
pub mod io_extras {
98100
use core2::io::{self, Read, Write};
99101

100102
/// A writer which will move data into the void.
@@ -154,6 +156,8 @@ mod io_extras {
154156
}
155157

156158
#[cfg(feature = "std")]
159+
#[doc(hidden)]
160+
/// IO utilities public only for use by in-crate macros. These should not be used externally
157161
mod io_extras {
158162
pub fn read_to_end<D: ::std::io::Read>(mut d: D) -> Result<Vec<u8>, ::std::io::Error> {
159163
let mut buf = Vec::new();

lightning/src/util/ser_macros.rs

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -354,58 +354,78 @@ macro_rules! _check_missing_tlv {
354354
#[doc(hidden)]
355355
#[macro_export]
356356
macro_rules! _decode_tlv {
357-
($reader: expr, $field: ident, (default_value, $default: expr)) => {{
358-
$crate::_decode_tlv!($reader, $field, required)
357+
($outer_reader: expr, $reader: expr, $field: ident, (default_value, $default: expr)) => {{
358+
$crate::_decode_tlv!($outer_reader, $reader, $field, required)
359359
}};
360-
($reader: expr, $field: ident, (static_value, $value: expr)) => {{
360+
($outer_reader: expr, $reader: expr, $field: ident, (static_value, $value: expr)) => {{
361361
}};
362-
($reader: expr, $field: ident, required) => {{
362+
($outer_reader: expr, $reader: expr, $field: ident, required) => {{
363363
$field = $crate::util::ser::Readable::read(&mut $reader)?;
364364
}};
365-
($reader: expr, $field: ident, (required: $trait: ident $(, $read_arg: expr)?)) => {{
365+
($outer_reader: expr, $reader: expr, $field: ident, (required: $trait: ident $(, $read_arg: expr)?)) => {{
366366
$field = $trait::read(&mut $reader $(, $read_arg)*)?;
367367
}};
368-
($reader: expr, $field: ident, required_vec) => {{
368+
($outer_reader: expr, $reader: expr, $field: ident, required_vec) => {{
369369
let f: $crate::util::ser::WithoutLength<Vec<_>> = $crate::util::ser::Readable::read(&mut $reader)?;
370370
$field = f.0;
371371
}};
372-
($reader: expr, $field: ident, option) => {{
372+
($outer_reader: expr, $reader: expr, $field: ident, option) => {{
373373
$field = Some($crate::util::ser::Readable::read(&mut $reader)?);
374374
}};
375-
($reader: expr, $field: ident, optional_vec) => {{
375+
($outer_reader: expr, $reader: expr, $field: ident, optional_vec) => {{
376376
let f: $crate::util::ser::WithoutLength<Vec<_>> = $crate::util::ser::Readable::read(&mut $reader)?;
377377
$field = Some(f.0);
378378
}};
379379
// `upgradable_required` indicates we're reading a required TLV that may have been upgraded
380380
// without backwards compat. We'll error if the field is missing, and return `Ok(None)` if the
381381
// field is present but we can no longer understand it.
382382
// Note that this variant can only be used within a `MaybeReadable` read.
383-
($reader: expr, $field: ident, upgradable_required) => {{
383+
($outer_reader: expr, $reader: expr, $field: ident, upgradable_required) => {{
384384
$field = match $crate::util::ser::MaybeReadable::read(&mut $reader)? {
385385
Some(res) => res,
386-
_ => return Ok(None)
386+
None => {
387+
// If we successfully read a value but we don't know how to parse it, we give up
388+
// and immediately return `None`. However, we need to make sure we read the correct
389+
// number of bytes for this TLV stream, which is implicitly the end of the stream.
390+
// Thus, we consume everything left in the `$outer_reader` here, ensuring that if
391+
// we're being read as a part of another TLV stream we don't spuriously fail to
392+
// deserialize the outer object due to a TLV length mismatch.
393+
$crate::io_extras::copy($outer_reader, &mut $crate::io_extras::sink()).unwrap();
394+
return Ok(None)
395+
},
387396
};
388397
}};
389398
// `upgradable_option` indicates we're reading an Option-al TLV that may have been upgraded
390399
// without backwards compat. $field will be None if the TLV is missing or if the field is present
391400
// but we can no longer understand it.
392-
($reader: expr, $field: ident, upgradable_option) => {{
401+
($outer_reader: expr, $reader: expr, $field: ident, upgradable_option) => {{
393402
$field = $crate::util::ser::MaybeReadable::read(&mut $reader)?;
403+
if $field.is_none() {
404+
#[cfg(not(debug_assertions))] {
405+
// In general, MaybeReadable implementations are required to consume all the bytes
406+
// of the object even if they don't understand it, but due to a bug in the
407+
// serialization format for `impl_writeable_tlv_based_enum_upgradable` we sometimes
408+
// don't know how many bytes that is. In such cases, we'd like to spuriously allow
409+
// TLV length mismatches, which we do here by calling `eat_remaining` so that the
410+
// `s.bytes_remain()` check in `_decode_tlv_stream_range` doesn't fail.
411+
$reader.eat_remaining()?;
412+
}
413+
}
394414
}};
395-
($reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
415+
($outer_reader: expr, $reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
396416
$field = Some($trait::read(&mut $reader $(, $read_arg)*)?);
397417
}};
398-
($reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident, $encoder:ty))) => {{
399-
$crate::_decode_tlv!($reader, $field, (option, encoding: ($fieldty, $encoding)));
418+
($outer_reader: expr, $reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident, $encoder:ty))) => {{
419+
$crate::_decode_tlv!($outer_reader, $reader, $field, (option, encoding: ($fieldty, $encoding)));
400420
}};
401-
($reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident))) => {{
421+
($outer_reader: expr, $reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident))) => {{
402422
$field = {
403423
let field: $encoding<$fieldty> = ser::Readable::read(&mut $reader)?;
404424
Some(field.0)
405425
};
406426
}};
407-
($reader: expr, $field: ident, (option, encoding: $fieldty: ty)) => {{
408-
$crate::_decode_tlv!($reader, $field, option);
427+
($outer_reader: expr, $reader: expr, $field: ident, (option, encoding: $fieldty: ty)) => {{
428+
$crate::_decode_tlv!($outer_reader, $reader, $field, option);
409429
}};
410430
}
411431

@@ -539,7 +559,7 @@ macro_rules! _decode_tlv_stream_range {
539559
let mut s = ser::FixedLengthReader::new(&mut stream_ref, length.0);
540560
match typ.0 {
541561
$(_t if $crate::_decode_tlv_stream_match_check!(_t, $type, $fieldty) => {
542-
$crate::_decode_tlv!(s, $field, $fieldty);
562+
$crate::_decode_tlv!($stream, s, $field, $fieldty);
543563
if s.bytes_remain() {
544564
s.eat_remaining()?; // Return ShortRead if there's actually not enough bytes
545565
return Err(DecodeError::InvalidValue);
@@ -1065,6 +1085,10 @@ macro_rules! impl_writeable_tlv_based_enum {
10651085
/// when [`MaybeReadable`] is practical instead of just [`Readable`] as it provides an upgrade path for
10661086
/// new variants to be added which are simply ignored by existing clients.
10671087
///
1088+
/// Note that only struct and unit variants (not tuple variants) will support downgrading, thus any
1089+
/// new odd variants MUST be non-tuple (i.e. described using `$variant_id` and `$variant_name` not
1090+
/// `$tuple_variant_id` and `$tuple_variant_name`).
1091+
///
10681092
/// [`MaybeReadable`]: crate::util::ser::MaybeReadable
10691093
/// [`Writeable`]: crate::util::ser::Writeable
10701094
/// [`DecodeError::UnknownRequiredFeature`]: crate::ln::msgs::DecodeError::UnknownRequiredFeature
@@ -1102,7 +1126,14 @@ macro_rules! impl_writeable_tlv_based_enum_upgradable {
11021126
$($($tuple_variant_id => {
11031127
Ok(Some($st::$tuple_variant_name(Readable::read(reader)?)))
11041128
}),*)*
1105-
_ if id % 2 == 1 => Ok(None),
1129+
_ if id % 2 == 1 => {
1130+
// Assume that a $variant_id was written, not a $tuple_variant_id, and read
1131+
// the length prefix and discard the correct number of bytes.
1132+
let tlv_len: $crate::util::ser::BigSize = $crate::util::ser::Readable::read(reader)?;
1133+
let mut rd = $crate::util::ser::FixedLengthReader::new(reader, tlv_len.0);
1134+
rd.eat_remaining().map_err(|_| $crate::ln::msgs::DecodeError::ShortRead)?;
1135+
Ok(None)
1136+
},
11061137
_ => Err($crate::ln::msgs::DecodeError::UnknownRequiredFeature),
11071138
}
11081139
}

0 commit comments

Comments
 (0)