-
Notifications
You must be signed in to change notification settings - Fork 163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PE: fix address size incompatibility on 32-bit builds #441
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -119,7 +119,7 @@ pub struct StandardFields { | |
/// * For device drivers, this is the address of the initialization function. | ||
/// | ||
/// The entry point function is optional for DLLs. When no entry point is present, this member is zero. | ||
pub address_of_entry_point: u64, | ||
pub address_of_entry_point: u32, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this seems like the only actually required fix, but even that is somewhat unclear to me? everything else seems to push the usize <-> u64/u32 casting down into the However, there was a certain clarity of passing a Could you elaborate more on why this gets downgraded to a u32 though? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Theoretically, this
Footnotes |
||
/// A pointer to the beginning of the code section (.text), relative to the image base. | ||
pub base_of_code: u64, | ||
/// A pointer to the beginning of the data section (.data), relative to the image base. Absent in 64-bit PE32+. | ||
|
@@ -139,7 +139,7 @@ impl From<StandardFields32> for StandardFields { | |
size_of_code: u64::from(fields.size_of_code), | ||
size_of_initialized_data: u64::from(fields.size_of_initialized_data), | ||
size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data), | ||
address_of_entry_point: u64::from(fields.address_of_entry_point), | ||
address_of_entry_point: fields.address_of_entry_point, | ||
base_of_code: u64::from(fields.base_of_code), | ||
base_of_data: fields.base_of_data, | ||
} | ||
|
@@ -171,7 +171,7 @@ impl From<StandardFields64> for StandardFields { | |
size_of_code: u64::from(fields.size_of_code), | ||
size_of_initialized_data: u64::from(fields.size_of_initialized_data), | ||
size_of_uninitialized_data: u64::from(fields.size_of_uninitialized_data), | ||
address_of_entry_point: u64::from(fields.address_of_entry_point), | ||
address_of_entry_point: fields.address_of_entry_point, | ||
base_of_code: u64::from(fields.base_of_code), | ||
base_of_data: 0, | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -166,7 +166,7 @@ impl ImageTlsDirectory { | |
impl<'a> TlsData<'a> { | ||
pub fn parse<T: Sized>( | ||
bytes: &'a [u8], | ||
image_base: usize, | ||
image_base: u64, | ||
dd: &data_directories::DataDirectory, | ||
sections: &[section_table::SectionTable], | ||
file_alignment: u32, | ||
|
@@ -183,7 +183,7 @@ impl<'a> TlsData<'a> { | |
|
||
pub fn parse_with_opts<T: Sized>( | ||
bytes: &'a [u8], | ||
image_base: usize, | ||
image_base: u64, | ||
dd: &data_directories::DataDirectory, | ||
sections: &[section_table::SectionTable], | ||
file_alignment: u32, | ||
|
@@ -208,18 +208,18 @@ impl<'a> TlsData<'a> { | |
))); | ||
} | ||
|
||
if (itd.start_address_of_raw_data as usize) < image_base { | ||
if itd.start_address_of_raw_data < image_base { | ||
return Err(error::Error::Malformed(format!( | ||
"tls start_address_of_raw_data ({:#x}) is less than image base ({:#x})", | ||
itd.start_address_of_raw_data, image_base | ||
))); | ||
} | ||
|
||
// VA to RVA | ||
let rva = itd.start_address_of_raw_data as usize - image_base; | ||
let rva = itd.start_address_of_raw_data - image_base; | ||
let size = itd.end_address_of_raw_data - itd.start_address_of_raw_data; | ||
let offset = | ||
utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| { | ||
let offset = utils::find_offset(rva as usize, sections, file_alignment, opts) | ||
.ok_or_else(|| { | ||
error::Error::Malformed(format!( | ||
"cannot map tls start_address_of_raw_data rva ({:#x}) into offset", | ||
rva | ||
|
@@ -230,32 +230,32 @@ impl<'a> TlsData<'a> { | |
|
||
// Parse the index if any | ||
if itd.address_of_index != 0 { | ||
if (itd.address_of_index as usize) < image_base { | ||
if itd.address_of_index < image_base { | ||
return Err(error::Error::Malformed(format!( | ||
"tls address_of_index ({:#x}) is less than image base ({:#x})", | ||
itd.address_of_index, image_base | ||
))); | ||
} | ||
|
||
// VA to RVA | ||
let rva = itd.address_of_index as usize - image_base; | ||
let offset = utils::find_offset(rva, sections, file_alignment, opts); | ||
let rva = itd.address_of_index - image_base; | ||
let offset = utils::find_offset(rva as usize, sections, file_alignment, opts); | ||
slot = offset.and_then(|x| bytes.pread_with::<u32>(x, scroll::LE).ok()); | ||
} | ||
|
||
// Parse the callbacks if any | ||
if itd.address_of_callbacks != 0 { | ||
if (itd.address_of_callbacks as usize) < image_base { | ||
if itd.address_of_callbacks < image_base { | ||
return Err(error::Error::Malformed(format!( | ||
"tls address_of_callbacks ({:#x}) is less than image base ({:#x})", | ||
itd.address_of_callbacks, image_base | ||
))); | ||
} | ||
|
||
// VA to RVA | ||
let rva = itd.address_of_callbacks as usize - image_base; | ||
let offset = | ||
utils::find_offset(rva, sections, file_alignment, opts).ok_or_else(|| { | ||
let rva = itd.address_of_callbacks - image_base; | ||
let offset = utils::find_offset(rva as usize, sections, file_alignment, opts) | ||
.ok_or_else(|| { | ||
error::Error::Malformed(format!( | ||
"cannot map tls address_of_callbacks rva ({:#x}) into offset", | ||
rva | ||
|
@@ -279,11 +279,11 @@ impl<'a> TlsData<'a> { | |
))); | ||
} | ||
// Each callback is an VA so convert it to RVA | ||
// For x86 compatibility, `usize` is 32-bit, `u64` is 64-bit. | ||
// Therefore upcast to u64 first, then downcast the whole var to u32. | ||
let callback_rva = (callback - image_base as u64) as usize; | ||
let callback_rva = callback - image_base; | ||
// Check if the callback is in the image | ||
if utils::find_offset(callback_rva, sections, file_alignment, opts).is_none() { | ||
if utils::find_offset(callback_rva as usize, sections, file_alignment, opts) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't there technically still an issue on 32-bit systems if tbh i'm not sure we ever supported analyzing 64-bit binaries on on 32-bit systems, simply due to rust There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yes, where the case the image must be broken. We shouldn't assume any of TLS callback addresses to be a valid VA/RVA. We can maybe put the check there. |
||
.is_none() | ||
{ | ||
return Err(error::Error::Malformed(format!( | ||
"cannot map tls callback ({:#x})", | ||
callback | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this the actual bug? (that we used a
usize
but on 32-bit systems, this can beu64
?)If yes, is it really required for
entry
to be fixed to u32? If there's a compelling reason (For consistency perhaps) then it seems fine, but this is a pretty big breaking change, but it may be worth it if it prevents future issues.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this has been a serious hidden bug for long.
Fortunately, RVA calculations (i.e., TLS callbacks map) work fine even it overflows. (i.e.,
0x140000000u64
overflows and trimmed into0x40000000u32
, then subtract theu32
RVA)On the other hand, this is a significant correctness issue likewise whenever the consumer wants to use it for something like verification.
I guess this has been invisible for this long because no one really cares about 32-bit builds.