Skip to content
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: Make TLS callbacks iterator instead of Vec<T> #427

Open
kkent030315 opened this issue Oct 29, 2024 · 0 comments
Open

PE: Make TLS callbacks iterator instead of Vec<T> #427

kkent030315 opened this issue Oct 29, 2024 · 0 comments

Comments

@kkent030315
Copy link
Contributor

kkent030315 commented Oct 29, 2024

pub callbacks: Vec<u64>,

I'd like to see this in next breaking change rollup if the callbacks field should be something like TLSCallbackIterator rather than a Vec<T> which requires alloc, so we can make TlsData derive Copy, Clone, Pread and Pwrite.

The size of entries can only known at parsing e.g., as following; so it still can raise errors for malformed callback entry when PE::parse*, not when the iterator is called.

Optionally, we can make callbacks virtual address validation (if utils::find_offset(callback.wrapping_sub...) configurable in opts like opts.validate_tls_callbacks_va so validation won't occur when consumers does not want it w.r.t. intentionally malformed binaries.

/// TLS information.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct TlsData<'a> {
    /// TLS directory.
    pub image_tls_directory: ImageTlsDirectory,
    /// Raw data of the TLS.
    pub raw_data: Option<&'a [u8]>,
    /// TLS index.
    pub slot: Option<u32>,
    /// Raw data of TLS callbacks elements without null-terminator element.
    pub callbacks_data: &'a [u8],
}

pub struct TlsCallbacksIterator<'a> {
    pub is_x64: bool,
    pub data: &'a [u8],
}

impl Iterator for TlsCallbacksIterator<'_> {}

impl<'a> TlsData<'a> {
    /// Returns iterator for [`ImageTlsDirectory::address_of_callbacks`]
    pub fn callbacks(&self) -> TlsCallbacksIterator<'a> {
        TlsCallbacksIterator { data: &self.callbacks_data }
    }
}
        // Parse the callbacks if any
        if itd.address_of_callbacks != 0 {
            if (itd.address_of_callbacks as usize) < 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(|| {
                    error::Error::Malformed(format!(
                        "cannot map tls address_of_callbacks rva ({:#x}) into offset",
                        rva
                    ))
                })?;

            let num_callbacks = bytes[offset..]
                .chunks(if is_64 {
                    core::mem::size_of::<u64>()
                } else {
                    core::mem::size_of::<u32>()
                })
                // Find null-terminator
                .take_while(|chunk| {
                    if is_64 {
                        chunk.pread_with::<u64>(0, scroll::LE)
                    } else {
                        chunk.pread_with::<u32>(0, scroll::LE).map(|v| v as u64)
                    }
                    .map(|x| x != 0)
                    .unwrap_or(false)
                })
                // Read callback entry from the byte slice
                .map(|chunk| {
                    if is_64 {
                        chunk.pread_with::<u64>(0, scroll::LE)
                    } else {
                        chunk.pread_with::<u32>(0, scroll::LE).map(|v| v as u64)
                    }
                    .map_err(|e| e.into())
                })
                // Maps malformed callback if any
                .map(|x| {
                    x.and_then(|callback| {
                        if callback == 0 {
                            return Ok(callback);
                        }

                        if callback < image_base as u64 {
                            return Err(error::Error::Malformed(format!(
                                "tls callback ({:#x}) is less than image base ({:#x})",
                                callback, image_base
                            )));
                        }

                        if utils::find_offset(
                            callback.wrapping_sub(image_base as u64) as usize,
                            sections,
                            file_alignment,
                            opts,
                        )
                        .is_none()
                        {
                            return Err(error::Error::Malformed(format!(
                                "cannot map tls callback ({:#x})",
                                callback
                            )));
                        }

                        Ok(callback)
                    })
                })
                .collect::<Result<Vec<_>, _>>()?
                .len();

            let callbacks_size = if is_64 {
                core::mem::size_of::<u64>()
            } else {
                core::mem::size_of::<u32>()
            };
            let callbacks_data = &bytes[offset..offset + num_callbacks * callbacks_size];
        }
@kkent030315 kkent030315 changed the title Make TLS callbacks iterator instead of Vec<T> PE: Make TLS callbacks iterator instead of Vec<T> Oct 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant