From f9ea4881ed5e4122d8e85ed88bbd78a08d1d5854 Mon Sep 17 00:00:00 2001 From: Jacob Latonis Date: Wed, 26 Jun 2024 22:14:38 -0600 Subject: [PATCH 1/5] refactor: iterative DFS instead of recursion for Mach-O exports --- lib/src/modules/macho/parser.rs | 149 +++++++++++------- ...8bfaae4d21de61f776e2405324c498ef52b21b.out | 70 ++++---- .../macho/tests/testdata/tiny_universal.out | 8 +- 3 files changed, 130 insertions(+), 97 deletions(-) diff --git a/lib/src/modules/macho/parser.rs b/lib/src/modules/macho/parser.rs index 31a212fcd..4f4450351 100644 --- a/lib/src/modules/macho/parser.rs +++ b/lib/src/modules/macho/parser.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::modules::protos; use bstr::{BStr, ByteSlice}; use itertools::Itertools; @@ -917,67 +919,94 @@ impl<'a> MachOFile<'a> { fn parse_export_node( &mut self, - ) -> impl FnMut(&'a [u8], u64, &BStr) -> IResult<&'a [u8], String> + '_ - { - move |data: &'a [u8], offset: u64, prefix: &BStr| { - let (remainder, length) = uleb128(&data[offset as usize..])?; - let mut remaining_data = remainder; - - if length != 0 { - let (remainder, flags) = uleb128(remaining_data)?; - match flags { - EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => { - let (remainder, _stub_offset) = uleb128(remainder)?; - - let (remainder, _resolver_offset) = - uleb128(remainder)?; - remaining_data = remainder; - } - EXPORT_SYMBOL_FLAGS_REEXPORT => { - let (remainder, _ordinal) = uleb128(remainder)?; + ) -> impl FnMut(&'a [u8], u64) -> IResult<&'a [u8], usize> + '_ { + move |data: &'a [u8], offset: u64| { + let mut stack = Vec::::new(); + let mut visited = HashMap::::new(); + stack.push(ExportNode { + offset: offset as usize, + prefix: "".to_string(), + }); + + while !data.is_empty() + && (offset as usize) < data.len() + && !stack.is_empty() + { + if let Some(export_node) = stack.pop() { + let (remainder, length) = + uleb128(&data[export_node.offset..])?; + let mut remaining_data = remainder; + if !visited.contains_key(&export_node.offset) { + visited.insert(export_node.offset, true); + if length != 0 { + let (remainder, flags) = uleb128(remaining_data)?; + match flags { + EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => { + let (remainder, _stub_offset) = + uleb128(remainder)?; + + let (remainder, _resolver_offset) = + uleb128(remainder)?; + remaining_data = remainder; + } + EXPORT_SYMBOL_FLAGS_REEXPORT => { + let (remainder, _ordinal) = + uleb128(remainder)?; + + let (remainder, _label) = map( + tuple(( + take_till(|b| b == b'\x00'), + tag(b"\x00"), + )), + |(s, _)| s, + )( + remainder + )?; + + remaining_data = remainder; + } + EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION => { + let (remainder, _offset) = + uleb128(remainder)?; + remaining_data = remainder; + } + _ => {} + } + } - let (remainder, _label) = map( - tuple((take_till(|b| b == b'\x00'), tag(b"\x00"))), - |(s, _)| s, - )( - remainder - )?; + let (remainder, edges) = u8(remaining_data)?; + let mut edge_remainder = remainder; + for _ in 0..edges { + let (remainder, strr) = map( + tuple(( + take_till(|b| b == b'\x00'), + tag(b"\x00"), + )), + |(s, _)| s, + )( + edge_remainder + )?; + let edge_label = BStr::new(strr); + let (remainder, edge_offset) = uleb128(remainder)?; + if let Ok(edge_label_str) = edge_label.to_str() { + stack.push(ExportNode { + offset: edge_offset as usize, + prefix: format!( + "{}{}", + export_node.prefix, edge_label_str + ), + }); + } + edge_remainder = remainder; + } - remaining_data = remainder; - } - EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION => { - let (remainder, _offset) = uleb128(remainder)?; - remaining_data = remainder; + if length != 0 { + self.exports.push(export_node.prefix) + } } - _ => {} - } - } - - let (remainder, edges) = u8(remaining_data)?; - let mut edge_remainder = remainder; - - for _ in 0..edges { - let (remainder, strr) = map( - tuple((take_till(|b| b == b'\x00'), tag(b"\x00"))), - |(s, _)| s, - )(edge_remainder)?; - let edge_label = BStr::new(strr); - let (remainder, edge_offset) = uleb128(remainder)?; - let (_, _) = self.parse_export_node()( - data, - edge_offset, - BStr::new(&bstr::concat([prefix, edge_label])), - )?; - edge_remainder = remainder; - } - - if length != 0 { - if let Ok(prefix) = prefix.to_str() { - self.exports.push(prefix.to_string()) } } - - Ok((data, prefix.to_str().unwrap().to_string())) + Ok((data, 0)) } } @@ -988,8 +1017,7 @@ impl<'a> MachOFile<'a> { ) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Vec> + '_ { move |data: &'a [u8]| { let exports = Vec::::new(); - let (remainder, _) = - self.parse_export_node()(data, 0, BStr::new(""))?; + let (remainder, _) = self.parse_export_node()(data, 0)?; Ok((remainder, exports)) } @@ -1394,6 +1422,11 @@ struct MinVersion { sdk: u32, } +struct ExportNode { + offset: usize, + prefix: String, +} + /// Parser that reads a 32-bits or 64-bits fn uint( endianness: Endianness, diff --git a/lib/src/modules/macho/tests/testdata/01ac68a14f0ff5faa72bb33e768bfaae4d21de61f776e2405324c498ef52b21b.out b/lib/src/modules/macho/tests/testdata/01ac68a14f0ff5faa72bb33e768bfaae4d21de61f776e2405324c498ef52b21b.out index ed87865e7..aab485277 100644 --- a/lib/src/modules/macho/tests/testdata/01ac68a14f0ff5faa72bb33e768bfaae4d21de61f776e2405324c498ef52b21b.out +++ b/lib/src/modules/macho/tests/testdata/01ac68a14f0ff5faa72bb33e768bfaae4d21de61f776e2405324c498ef52b21b.out @@ -456,39 +456,39 @@ min_version: version: "10.13.0" sdk: "10.13.0" exports: - - "__Z18AllocateStringCopyRPcRK8wxString" - - "__Z18ACCT_GetFirstIndexPK15CAPF_DataSourceRl" - - "__Z19CBB_GetResmanEventsP15CAPF_DataSourcePFvP20HarmonyResourceEventPvES3_P15HarmonyNBTicket" - - "__Z19CBB_SetEFControlBarP15CAPF_DataSourceP15CAPF_PluginInfoPv" - - "__Z19CB_ImportControlBarP15CAPF_DataSourceP15CAPF_PluginInfoPv" - - "__Z19ACCT_FreeStringListPP17HarmonyAttributesi" - - "__Z19ACCT_ClearJobLogXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" - - "__Z19ACCT_GetEventsExXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" - - "__Z16CreateControlBarP8stCBInfoP20HarmonySessionHandle" - - "__Z16ACCT_ClearJobLogPK15CAPF_DataSource" - - "__Z16out_HarmonyAttrsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEEiP17HarmonyAttributes" - - "__Z14ACCT_GetJobLogPK15CAPF_DataSourcePPP17HarmonyAttributesRl" - - "__Z10InitializeP15CAPF_PluginInfoP14PLUGINMEMBLOCK" - - "__Z17ACCT_GetJobLogXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" - - "__Z21CBB_GetControlBarDataP15CAPF_DataSourceP15CAPF_PluginInfoPv" - - "__Z21ACCT_GetJobLogPortionPK15CAPF_DataSourceRlS2_PPP17HarmonyAttributesS2_" - - "__Z21ACCT_GetFirstIndexXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" - - "__Z21APF_ds2HarmonyHandlesPK15CAPF_DataSourcePP29harmonyDataSourceSpecificDataPP19HarmonyServerHandle" - - "__Z21APF_harmonyGetSessionP29harmonyDataSourceSpecificData22APF_harmonySessionType" - - "__Z20CBB_DeleteControlBarP15CAPF_DataSourceRK8wxString" - - "__Z20ACCT_GetJobLogLengthPK15CAPF_DataSourceRl" - - "__Z22CBB_FreeControlBarDataP15CAPF_DataSourceP15CAPF_PluginInfoPv" - - "__Z22ACCT_FreeStringListXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" - - "__Z24CBB_GetControlbarSupportP15CAPF_DataSourceP15CAPF_PluginInfoPv" - - "__Z24ACCT_GetJobLogPortionXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" - - "__Z23ACCT_GetJobLogLengthXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" - - "__Z23APF_ChkTicketOrTimedOutP15CAPF_DataSourceR15HarmonyNBTicketR13HarmonyResult" - - "__Z23GetFeatureSessionHandlePK15CAPF_DataSource22APF_harmonySessionTypePP20HarmonySessionHandle" - - "__Z25APF_H_GetLocalizedStringsP29harmonyDataSourceSpecificDataPPciS2_i" - - "__Z26APF_H_GetLocalizedStringNBP15CAPF_DataSourceR8wxStringS2_iS2_" - - "__Z36APF_H_GetLocalizedStringNB_AlternateP15CAPF_DataSourceR8wxStringS2_iS2_" - - "__ZlsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEEP17HarmonyAttributes" - - "_APF_Plugin_Initialize" - - "_APF_Plugin_DisconnectingDataSource" + - "_APF_Plugin_Unload" - "_APF_Plugin_Terminate" - - "_APF_Plugin_Unload" \ No newline at end of file + - "_APF_Plugin_DisconnectingDataSource" + - "_APF_Plugin_Initialize" + - "__ZlsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEEP17HarmonyAttributes" + - "__Z36APF_H_GetLocalizedStringNB_AlternateP15CAPF_DataSourceR8wxStringS2_iS2_" + - "__Z26APF_H_GetLocalizedStringNBP15CAPF_DataSourceR8wxStringS2_iS2_" + - "__Z25APF_H_GetLocalizedStringsP29harmonyDataSourceSpecificDataPPciS2_i" + - "__Z23GetFeatureSessionHandlePK15CAPF_DataSource22APF_harmonySessionTypePP20HarmonySessionHandle" + - "__Z23APF_ChkTicketOrTimedOutP15CAPF_DataSourceR15HarmonyNBTicketR13HarmonyResult" + - "__Z23ACCT_GetJobLogLengthXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" + - "__Z24ACCT_GetJobLogPortionXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" + - "__Z24CBB_GetControlbarSupportP15CAPF_DataSourceP15CAPF_PluginInfoPv" + - "__Z22ACCT_FreeStringListXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" + - "__Z22CBB_FreeControlBarDataP15CAPF_DataSourceP15CAPF_PluginInfoPv" + - "__Z20ACCT_GetJobLogLengthPK15CAPF_DataSourceRl" + - "__Z20CBB_DeleteControlBarP15CAPF_DataSourceRK8wxString" + - "__Z21APF_harmonyGetSessionP29harmonyDataSourceSpecificData22APF_harmonySessionType" + - "__Z21APF_ds2HarmonyHandlesPK15CAPF_DataSourcePP29harmonyDataSourceSpecificDataPP19HarmonyServerHandle" + - "__Z21ACCT_GetFirstIndexXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" + - "__Z21ACCT_GetJobLogPortionPK15CAPF_DataSourceRlS2_PPP17HarmonyAttributesS2_" + - "__Z21CBB_GetControlBarDataP15CAPF_DataSourceP15CAPF_PluginInfoPv" + - "__Z17ACCT_GetJobLogXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" + - "__Z10InitializeP15CAPF_PluginInfoP14PLUGINMEMBLOCK" + - "__Z14ACCT_GetJobLogPK15CAPF_DataSourcePPP17HarmonyAttributesRl" + - "__Z16out_HarmonyAttrsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEEiP17HarmonyAttributes" + - "__Z16ACCT_ClearJobLogPK15CAPF_DataSource" + - "__Z16CreateControlBarP8stCBInfoP20HarmonySessionHandle" + - "__Z19ACCT_GetEventsExXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" + - "__Z19ACCT_ClearJobLogXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv" + - "__Z19ACCT_FreeStringListPP17HarmonyAttributesi" + - "__Z19CB_ImportControlBarP15CAPF_DataSourceP15CAPF_PluginInfoPv" + - "__Z19CBB_SetEFControlBarP15CAPF_DataSourceP15CAPF_PluginInfoPv" + - "__Z19CBB_GetResmanEventsP15CAPF_DataSourcePFvP20HarmonyResourceEventPvES3_P15HarmonyNBTicket" + - "__Z18ACCT_GetFirstIndexPK15CAPF_DataSourceRl" + - "__Z18AllocateStringCopyRPcRK8wxString" \ No newline at end of file diff --git a/lib/src/modules/macho/tests/testdata/tiny_universal.out b/lib/src/modules/macho/tests/testdata/tiny_universal.out index 2c239ce86..adecfda64 100644 --- a/lib/src/modules/macho/tests/testdata/tiny_universal.out +++ b/lib/src/modules/macho/tests/testdata/tiny_universal.out @@ -193,9 +193,9 @@ file: version: "10.9.0" sdk: "10.10.0" exports: - - "__mh_execute_header" - - "_factorial" - "_main" + - "_factorial" + - "__mh_execute_header" - magic: 0xcffaedfe cputype: 0x1000007 cpusubtype: 0x80000003 @@ -395,6 +395,6 @@ file: version: "10.9.0" sdk: "10.10.0" exports: - - "__mh_execute_header" + - "_main" - "_factorial" - - "_main" \ No newline at end of file + - "__mh_execute_header" \ No newline at end of file From 32d8ffeaad1853992c527cd52c9b263cedbde03b Mon Sep 17 00:00:00 2001 From: Jacob Latonis Date: Wed, 26 Jun 2024 22:26:28 -0600 Subject: [PATCH 2/5] fix clippy issues --- lib/src/modules/macho/parser.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/modules/macho/parser.rs b/lib/src/modules/macho/parser.rs index 4f4450351..8b5687f6b 100644 --- a/lib/src/modules/macho/parser.rs +++ b/lib/src/modules/macho/parser.rs @@ -936,8 +936,11 @@ impl<'a> MachOFile<'a> { let (remainder, length) = uleb128(&data[export_node.offset..])?; let mut remaining_data = remainder; - if !visited.contains_key(&export_node.offset) { - visited.insert(export_node.offset, true); + + if let std::collections::hash_map::Entry::Vacant(e) = + visited.entry(export_node.offset) + { + e.insert(true); if length != 0 { let (remainder, flags) = uleb128(remaining_data)?; match flags { From 1a3d186d141b04d8c95d1853e4b682750386fcd0 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 27 Jun 2024 19:01:09 +0200 Subject: [PATCH 3/5] refactor: use a `HashSet` instead of a `HashMap`. Also reduces the indentation levels in `parse_export_node`. --- lib/src/modules/macho/parser.rs | 143 ++++++++++++++++---------------- 1 file changed, 70 insertions(+), 73 deletions(-) diff --git a/lib/src/modules/macho/parser.rs b/lib/src/modules/macho/parser.rs index 8b5687f6b..2a3981bfc 100644 --- a/lib/src/modules/macho/parser.rs +++ b/lib/src/modules/macho/parser.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::HashSet; use crate::modules::protos; use bstr::{BStr, ByteSlice}; @@ -922,7 +922,8 @@ impl<'a> MachOFile<'a> { ) -> impl FnMut(&'a [u8], u64) -> IResult<&'a [u8], usize> + '_ { move |data: &'a [u8], offset: u64| { let mut stack = Vec::::new(); - let mut visited = HashMap::::new(); + let mut visited = HashSet::::new(); + stack.push(ExportNode { offset: offset as usize, prefix: "".to_string(), @@ -932,83 +933,79 @@ impl<'a> MachOFile<'a> { && (offset as usize) < data.len() && !stack.is_empty() { - if let Some(export_node) = stack.pop() { - let (remainder, length) = - uleb128(&data[export_node.offset..])?; - let mut remaining_data = remainder; - - if let std::collections::hash_map::Entry::Vacant(e) = - visited.entry(export_node.offset) - { - e.insert(true); - if length != 0 { - let (remainder, flags) = uleb128(remaining_data)?; - match flags { - EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => { - let (remainder, _stub_offset) = - uleb128(remainder)?; - - let (remainder, _resolver_offset) = - uleb128(remainder)?; - remaining_data = remainder; - } - EXPORT_SYMBOL_FLAGS_REEXPORT => { - let (remainder, _ordinal) = - uleb128(remainder)?; - - let (remainder, _label) = map( - tuple(( - take_till(|b| b == b'\x00'), - tag(b"\x00"), - )), - |(s, _)| s, - )( - remainder - )?; - - remaining_data = remainder; - } - EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION => { - let (remainder, _offset) = - uleb128(remainder)?; - remaining_data = remainder; - } - _ => {} - } - } + let export_node = stack.pop().unwrap(); - let (remainder, edges) = u8(remaining_data)?; - let mut edge_remainder = remainder; - for _ in 0..edges { - let (remainder, strr) = map( - tuple(( - take_till(|b| b == b'\x00'), - tag(b"\x00"), - )), - |(s, _)| s, - )( - edge_remainder - )?; - let edge_label = BStr::new(strr); - let (remainder, edge_offset) = uleb128(remainder)?; - if let Ok(edge_label_str) = edge_label.to_str() { - stack.push(ExportNode { - offset: edge_offset as usize, - prefix: format!( - "{}{}", - export_node.prefix, edge_label_str - ), - }); - } - edge_remainder = remainder; - } + // If node was already visited, continue without processing it. + if !visited.insert(export_node.offset) { + continue; + } + + let (remainder, length) = + uleb128(&data[export_node.offset..])?; - if length != 0 { - self.exports.push(export_node.prefix) + let mut remaining_data = remainder; + + if length != 0 { + let (remainder, flags) = uleb128(remaining_data)?; + match flags { + EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => { + let (remainder, _stub_offset) = + uleb128(remainder)?; + + let (remainder, _resolver_offset) = + uleb128(remainder)?; + remaining_data = remainder; + } + EXPORT_SYMBOL_FLAGS_REEXPORT => { + let (remainder, _ordinal) = uleb128(remainder)?; + + let (remainder, _label) = + map( + tuple(( + take_till(|b| b == b'\x00'), + tag(b"\x00"), + )), + |(s, _)| s, + )(remainder)?; + + remaining_data = remainder; } + EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION => { + let (remainder, _offset) = uleb128(remainder)?; + remaining_data = remainder; + } + _ => {} + } + } + + let (remainder, edges) = u8(remaining_data)?; + let mut edge_remainder = remainder; + + for _ in 0..edges { + let (remainder, strr) = + map( + tuple((take_till(|b| b == b'\x00'), tag(b"\x00"))), + |(s, _)| s, + )(edge_remainder)?; + let edge_label = BStr::new(strr); + let (remainder, edge_offset) = uleb128(remainder)?; + if let Ok(edge_label_str) = edge_label.to_str() { + stack.push(ExportNode { + offset: edge_offset as usize, + prefix: format!( + "{}{}", + export_node.prefix, edge_label_str + ), + }); } + edge_remainder = remainder; + } + + if length != 0 { + self.exports.push(export_node.prefix) } } + Ok((data, 0)) } } From 62357cf2fcd9b098b2a5e9539190f7c257da3c2a Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 27 Jun 2024 19:13:01 +0200 Subject: [PATCH 4/5] fix: avoid reading outside of `data`. `export_node.offset` can be arbitrary because it is read from the file and inserted in the stack without further verification. --- lib/src/modules/macho/parser.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/src/modules/macho/parser.rs b/lib/src/modules/macho/parser.rs index 2a3981bfc..c7d18dc11 100644 --- a/lib/src/modules/macho/parser.rs +++ b/lib/src/modules/macho/parser.rs @@ -940,10 +940,12 @@ impl<'a> MachOFile<'a> { continue; } - let (remainder, length) = - uleb128(&data[export_node.offset..])?; + let node_data = match data.get(export_node.offset..) { + Some(data) => data, + None => continue, + }; - let mut remaining_data = remainder; + let (mut remaining_data, length) = uleb128(node_data)?; if length != 0 { let (remainder, flags) = uleb128(remaining_data)?; @@ -978,8 +980,7 @@ impl<'a> MachOFile<'a> { } } - let (remainder, edges) = u8(remaining_data)?; - let mut edge_remainder = remainder; + let (mut edge_remainder, edges) = u8(remaining_data)?; for _ in 0..edges { let (remainder, strr) = From a06626cfe0857fea52f27c31b88ef7b1165432b7 Mon Sep 17 00:00:00 2001 From: "Victor M. Alvarez" Date: Thu, 27 Jun 2024 20:33:58 +0200 Subject: [PATCH 5/5] fix: panic while parsing ULEB128 numbers that are too large. --- lib/src/modules/macho/parser.rs | 76 ++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/lib/src/modules/macho/parser.rs b/lib/src/modules/macho/parser.rs index c7d18dc11..6f75e2a8f 100644 --- a/lib/src/modules/macho/parser.rs +++ b/lib/src/modules/macho/parser.rs @@ -1443,21 +1443,33 @@ fn uint( } } -/// Parser that reads ULEB128. -/// https://en.wikipedia.org/wiki/LEB128 +/// Parser that reads [ULEB128][1]. +/// +/// Notice however that this function returns a `u64`, is able to parse +/// number up to 72057594037927935. When parsing larger number it fails, +/// even if they are valid ULEB128. +/// +/// [1]: https://en.wikipedia.org/wiki/LEB128 fn uleb128(input: &[u8]) -> IResult<&[u8], u64> { let mut val: u64 = 0; - let mut shift: u64 = 0; + let mut shift: u32 = 0; let mut data = input; let mut byte: u8; loop { + // Read one byte of data. (data, byte) = u8(data)?; - val |= ((byte & !(1 << 7)) as u64) << shift; + // Use all the bits, except the most significant one. + let b = (byte & 0x7f) as u64; - if byte & (1 << 7) == 0 { + val |= b + .checked_shl(shift) + .ok_or(Err::Error(Error::new(input, ErrorKind::TooLarge)))?; + + // Break if the most significant bit is zero. + if byte & 0x80 == 0 { break; } @@ -1832,31 +1844,43 @@ impl From<&MinVersion> for protos::macho::MinVersion { #[test] fn test_uleb_parsing() { - let uleb_128_in = vec![0b1000_0001, 0b000_0001]; - let (_remainder, result) = uleb128(&uleb_128_in).unwrap(); - assert_eq!(129, result); + let (_, n) = uleb128(&[0b1000_0001, 0b000_0001]).unwrap(); + assert_eq!(129, n); + + let (_, n) = uleb128(&[0b1000_0000, 0b0000_0001]).unwrap(); + assert_eq!(128, n); + + let (_, n) = uleb128(&[0b111_1111]).unwrap(); + assert_eq!(127, n); + + let (_, n) = uleb128(&[0b111_1110]).unwrap(); + assert_eq!(126, n); + + let (_, n) = uleb128(&[0b000_0000]).unwrap(); + assert_eq!(0, n); + + let (_, n) = uleb128(&[0b1010_0000, 0b0000_0001]).unwrap(); + assert_eq!(160, n); - let uleb_128_in = vec![0b1000_0000, 0b0000_0001]; - let (_remainder, result) = uleb128(&uleb_128_in).unwrap(); - assert_eq!(128, result); + let (_, n) = uleb128(&[0b1001_0110, 0b0000_0101]).unwrap(); + assert_eq!(662, n); - let uleb_128_in = vec![0b111_1111]; - let (_remainder, result) = uleb128(&uleb_128_in).unwrap(); - assert_eq!(127, result); + let (_, n) = uleb128(&[0b1110_0101, 0b1000_1110, 0b0010_0110]).unwrap(); + assert_eq!(624485, n); - let uleb_128_in = vec![0b111_1110]; - let (_remainder, result) = uleb128(&uleb_128_in).unwrap(); - assert_eq!(126, result); + let (_, n) = uleb128(&[0x80, 0x80, 0x80, 0x00]).unwrap(); + assert_eq!(0, n); - let uleb_128_in = vec![0b000_0000]; - let (_remainder, result) = uleb128(&uleb_128_in).unwrap(); - assert_eq!(0, result); + let (_, n) = + uleb128(&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00]).unwrap(); + assert_eq!(0, n); - let uleb_128_in = vec![0b1010_0000, 0b0000_0001]; - let (_remainder, result) = uleb128(&uleb_128_in).unwrap(); - assert_eq!(160, result); + let (_, n) = + uleb128(&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]).unwrap(); + assert_eq!(72057594037927935, n); - let uleb_128_in = vec![0b10010110, 0b00000101]; - let (_remainder, result) = uleb128(&uleb_128_in).unwrap(); - assert_eq!(662, result); + assert!(uleb128(&[ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, + ]) + .is_err()); }