Skip to content

Commit

Permalink
read: use address size when checking address overflow (#733)
Browse files Browse the repository at this point in the history
Also, validate address sizes when reading them so that the address mask calculation is correct.
  • Loading branch information
philipc authored Jul 8, 2024
1 parent 38c185e commit 36ee540
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 48 deletions.
41 changes: 32 additions & 9 deletions src/read/aranges.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::common::{DebugArangesOffset, DebugInfoOffset, Encoding, SectionId};
use crate::endianity::Endianity;
use crate::read::{EndianSlice, Error, Range, Reader, ReaderOffset, Result, Section};
use crate::read::{
EndianSlice, Error, Range, Reader, ReaderAddress, ReaderOffset, Result, Section,
};

/// The `DebugAranges` struct represents the DWARF address range information
/// found in the `.debug_aranges` section.
Expand Down Expand Up @@ -157,7 +159,7 @@ where
}

let debug_info_offset = rest.read_offset(format).map(DebugInfoOffset)?;
let address_size = rest.read_u8()?;
let address_size = rest.read_address_size()?;
let segment_size = rest.read_u8()?;
if segment_size != 0 {
return Err(Error::UnsupportedSegmentSize);
Expand All @@ -170,9 +172,9 @@ where
// a multiple of the size of a single tuple (that is, twice the size of an address).
let tuple_length = address_size
.checked_mul(2)
.ok_or(Error::InvalidAddressRange)?;
.ok_or(Error::UnsupportedAddressSize(address_size))?;
if tuple_length == 0 {
return Err(Error::InvalidAddressRange);
return Err(Error::UnsupportedAddressSize(address_size));
}
let padding = if header_length % tuple_length == 0 {
0
Expand Down Expand Up @@ -296,8 +298,7 @@ impl ArangeEntry {
let begin = input.read_address(address_size)?;
let length = input.read_address(address_size)?;
// Calculate end now so that we can handle overflow.
// TODO: handle 32-bit address overflow as well.
let end = begin.checked_add(length).ok_or(Error::AddressOverflow)?;
let end = begin.add_sized(length, address_size)?;
let range = Range { begin, end };

match (begin, length) {
Expand Down Expand Up @@ -481,7 +482,7 @@ mod tests {

let error = ArangeHeader::parse(rest, DebugArangesOffset(0x10))
.expect_err("should fail to parse header");
assert_eq!(error, Error::InvalidAddressRange);
assert_eq!(error, Error::UnsupportedAddressSize(0xff));
}

#[test]
Expand Down Expand Up @@ -521,7 +522,7 @@ mod tests {

let error = ArangeHeader::parse(rest, DebugArangesOffset(0x10))
.expect_err("should fail to parse header");
assert_eq!(error, Error::InvalidAddressRange);
assert_eq!(error, Error::UnsupportedAddressSize(0));
}

#[test]
Expand Down Expand Up @@ -581,7 +582,29 @@ mod tests {
}

#[test]
fn test_parse_entry_overflow() {
fn test_parse_entry_overflow_32() {
let encoding = Encoding {
format: Format::Dwarf32,
version: 2,
address_size: 4,
};
#[rustfmt::skip]
let buf = [
// Address.
0x01, 0x02, 0x03, 0x84,
// Length.
0x05, 0x06, 0x07, 0x88,
// Next tuple.
0x09
];
let rest = &mut EndianSlice::new(&buf, LittleEndian);
let entry = ArangeEntry::parse(rest, encoding);
assert_eq!(*rest, EndianSlice::new(&buf[buf.len() - 1..], LittleEndian));
assert_eq!(entry, Err(Error::AddressOverflow));
}

#[test]
fn test_parse_entry_overflow_64() {
let encoding = Encoding {
format: Format::Dwarf32,
version: 2,
Expand Down
49 changes: 35 additions & 14 deletions src/read/cfi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use crate::common::{
use crate::constants::{self, DwEhPe};
use crate::endianity::Endianity;
use crate::read::{
EndianSlice, Error, Expression, Reader, ReaderOffset, Result, Section, StoreOnHeap,
EndianSlice, Error, Expression, Reader, ReaderAddress, ReaderOffset, Result, Section,
StoreOnHeap,
};

/// `DebugFrame` contains the `.debug_frame` section's frame unwinding
Expand Down Expand Up @@ -1341,7 +1342,7 @@ impl<R: Reader> CommonInformationEntry<R> {
let mut augmentation_string = rest.read_null_terminated_slice()?;

let address_size = if Section::has_address_and_segment_sizes(version) {
let address_size = rest.read_u8()?;
let address_size = rest.read_address_size()?;
let segment_size = rest.read_u8()?;
if segment_size != 0 {
return Err(Error::UnsupportedSegmentSize);
Expand Down Expand Up @@ -1805,7 +1806,8 @@ impl<R: Reader> FrameDescriptionEntry<R> {
/// This uses wrapping arithmetic, so the result may be less than
/// `initial_address`.
pub fn end_address(&self) -> u64 {
self.initial_address.wrapping_add(self.address_range)
self.initial_address
.wrapping_add_sized(self.address_range, self.cie.address_size)
}

/// The number of bytes of instructions that this entry has unwind
Expand Down Expand Up @@ -2198,6 +2200,7 @@ where
{
code_alignment_factor: Wrapping<u64>,
data_alignment_factor: Wrapping<i64>,
address_size: u8,
next_start_address: u64,
last_end_address: u64,
returned_last_row: bool,
Expand Down Expand Up @@ -2237,6 +2240,7 @@ where
UnwindTable {
code_alignment_factor: Wrapping(fde.cie().code_alignment_factor()),
data_alignment_factor: Wrapping(fde.cie().data_alignment_factor()),
address_size: fde.cie().address_size,
next_start_address: fde.initial_address(),
last_end_address: fde.end_address(),
returned_last_row: false,
Expand All @@ -2256,6 +2260,7 @@ where
UnwindTable {
code_alignment_factor: Wrapping(cie.code_alignment_factor()),
data_alignment_factor: Wrapping(cie.data_alignment_factor()),
address_size: cie.address_size,
next_start_address: 0,
last_end_address: 0,
returned_last_row: false,
Expand Down Expand Up @@ -2330,13 +2335,10 @@ where
}
AdvanceLoc { delta } => {
let delta = Wrapping(u64::from(delta)) * self.code_alignment_factor;
let address = self
self.next_start_address = self
.ctx
.start_address()
.checked_add(delta.0)
.ok_or(Error::AddressOverflow)?;

self.next_start_address = address;
.add_sized(delta.0, self.address_size)?;
self.ctx.row_mut().end_address = self.next_start_address;
return Ok(true);
}
Expand Down Expand Up @@ -3651,7 +3653,8 @@ fn parse_encoded_pointer<R: Reader>(
constants::DW_EH_PE_pcrel => {
if let Some(section_base) = parameters.bases.section {
let offset_from_section = input.offset_from(parameters.section);
section_base.wrapping_add(offset_from_section.into_u64())
section_base
.wrapping_add_sized(offset_from_section.into_u64(), parameters.address_size)
} else {
return Err(Error::PcRelativePointerButSectionBaseIsUndefined);
}
Expand Down Expand Up @@ -3682,7 +3685,10 @@ fn parse_encoded_pointer<R: Reader>(
};

let offset = parse_encoded_value(encoding, parameters, input)?;
Ok(Pointer::new(encoding, base.wrapping_add(offset)))
Ok(Pointer::new(
encoding,
base.wrapping_add_sized(offset, parameters.address_size),
))
}

fn parse_encoded_value<R: Reader>(
Expand Down Expand Up @@ -5379,8 +5385,23 @@ mod tests {
}

#[test]
fn test_eval_advance_loc_overflow() {
let cie = make_test_cie();
fn test_eval_advance_loc_overflow_32() {
let mut cie = make_test_cie();
cie.address_size = 4;
let mut ctx = UnwindContext::new();
ctx.row_mut().start_address = u32::MAX.into();
let expected = ctx.clone();
let instructions = [(
Err(Error::AddressOverflow),
CallFrameInstruction::AdvanceLoc { delta: 42 },
)];
assert_eval(ctx, expected, cie, None, instructions);
}

#[test]
fn test_eval_advance_loc_overflow_64() {
let mut cie = make_test_cie();
cie.address_size = 8;
let mut ctx = UnwindContext::new();
ctx.row_mut().start_address = u64::MAX;
let expected = ctx.clone();
Expand Down Expand Up @@ -7676,7 +7697,7 @@ mod tests {
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
address_size: 8,
section: &input,
};
assert_eq!(
Expand Down Expand Up @@ -7779,7 +7800,7 @@ mod tests {
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
address_size: 8,
section: &input,
};
assert_eq!(
Expand Down
32 changes: 22 additions & 10 deletions src/read/line.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use crate::common::{
};
use crate::constants;
use crate::endianity::Endianity;
use crate::read::{AttributeValue, EndianSlice, Error, Reader, ReaderOffset, Result, Section};
use crate::read::{
AttributeValue, EndianSlice, Error, Reader, ReaderAddress, ReaderOffset, Result, Section,
};

/// The `DebugLine` struct contains the source location to instruction mapping
/// found in the `.debug_line` section.
Expand Down Expand Up @@ -852,10 +854,8 @@ impl LineRow {

LineInstruction::FixedAddPc(operand) => {
if !self.tombstone {
self.address = self
.address
.checked_add(u64::from(operand))
.ok_or(Error::AddressOverflow)?;
let address_size = program.header().address_size();
self.address = self.address.add_sized(u64::from(operand), address_size)?;
self.op_index.0 = 0;
}
false
Expand Down Expand Up @@ -975,8 +975,7 @@ impl LineRow {
};
self.address = self
.address
.checked_add(address_advance.0)
.ok_or(Error::AddressOverflow)?;
.add_sized(address_advance.0, header.address_size())?;
Ok(())
}

Expand Down Expand Up @@ -1324,7 +1323,7 @@ where
}

if version >= 5 {
address_size = rest.read_u8()?;
address_size = rest.read_address_size()?;
let segment_selector_size = rest.read_u8()?;
if segment_selector_size != 0 {
return Err(Error::UnsupportedSegmentSize);
Expand Down Expand Up @@ -2639,8 +2638,21 @@ mod tests {
}

#[test]
fn test_exec_advance_pc_overflow() {
let header = make_test_header(EndianSlice::new(&[], LittleEndian));
fn test_exec_advance_pc_overflow_32() {
let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
header.encoding.address_size = 4;
let mut registers = LineRow::new(&header);
registers.address = u32::MAX.into();
let opcode = LineInstruction::AdvancePc(42);
let mut program = IncompleteLineProgram { header };
let result = registers.execute(opcode, &mut program);
assert_eq!(result, Err(Error::AddressOverflow));
}

#[test]
fn test_exec_advance_pc_overflow_64() {
let mut header = make_test_header(EndianSlice::new(&[], LittleEndian));
header.encoding.address_size = 8;
let mut registers = LineRow::new(&header);
registers.address = u64::MAX;
let opcode = LineInstruction::AdvancePc(42);
Expand Down
2 changes: 1 addition & 1 deletion src/read/lists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ fn parse_header<R: Reader>(input: &mut R) -> Result<ListsHeader> {
return Err(Error::UnknownVersion(u64::from(version)));
}

let address_size = input.read_u8()?;
let address_size = input.read_address_size()?;
let segment_selector_size = input.read_u8()?;
if segment_selector_size != 0 {
return Err(Error::UnsupportedSegmentSize);
Expand Down
9 changes: 5 additions & 4 deletions src/read/loclists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::constants;
use crate::endianity::Endianity;
use crate::read::{
lists::ListsHeader, DebugAddr, EndianSlice, Error, Expression, Range, RawRange, Reader,
ReaderOffset, ReaderOffsetId, Result, Section,
ReaderAddress, ReaderOffset, ReaderOffsetId, Result, Section,
};

/// The raw contents of the `.debug_loc` section.
Expand Down Expand Up @@ -593,7 +593,8 @@ impl<R: Reader> LocListIter<R> {
&mut self,
raw_loc: RawLocListEntry<R>,
) -> Result<Option<LocationListEntry<R>>> {
let mask = !0 >> (64 - self.raw.encoding.address_size * 8);
let address_size = self.raw.encoding.address_size;
let mask = u64::ones_sized(address_size);
let tombstone = if self.raw.encoding.version <= 4 {
mask - 1
} else {
Expand All @@ -620,7 +621,7 @@ impl<R: Reader> LocListIter<R> {
data,
} => {
let begin = self.get_address(begin)?;
let end = begin.wrapping_add(length) & mask;
let end = begin.wrapping_add_sized(length, address_size);
(Range { begin, end }, data)
}
RawLocListEntry::DefaultLocation { data } => (
Expand All @@ -645,7 +646,7 @@ impl<R: Reader> LocListIter<R> {
length,
data,
} => {
let end = begin.wrapping_add(length) & mask;
let end = begin.wrapping_add_sized(length, address_size);
(Range { begin, end }, data)
}
};
Expand Down
Loading

0 comments on commit 36ee540

Please sign in to comment.