diff options
| author | Mark Rowe <mark@vector35.com> | 2026-02-23 23:26:27 -0800 |
|---|---|---|
| committer | Mark Rowe <mark@vector35.com> | 2026-02-24 14:24:54 -0800 |
| commit | a72b98ed5a357bb380d81f07f3f36946aa729bb2 (patch) | |
| tree | ca48b3607721fa2c109b7989ae4b911bab89ea6d /view/sharedcache/core/MappedFileRegion.h | |
| parent | 3eda43f185a0411538745a99e251122e6a9192e0 (diff) | |
[DSC] Simplify file mapping to fix intermittent unrelocated pointers
The `FileAccessorCache`'s LRU eviction could discard a file's mapped
data after slide info had already been applied to it. Future accesses to
the file produced a fresh mapping, but failed to reapply the slide info.
This could result in pointers not correctly being slid, such as in
https://github.com/Vector35/binaryninja-api/issues/7689.
The LRU cache existed to stay within OS file descriptor limits, since
the old `MappedFile` held its fd open for the lifetime of the mapping.
There's no real reason for it to hold the file descriptor open like
this. Closing it after `mmap` is sufficient to avoid the file descriptor
limits.
`MappedFileRegion` replaces the combination of `FileAccessorCache`,
`WeakFileAccessor`, and `MappedFileAccessor`. It closes the fd
immediately after mmap, so all files can stay mapped without consuming
descriptors, making the cache unnecessary.
`MappedFileRegion` is owned directly by the `CacheEntry` for its full
lifetime. Slide info is applied exactly once to each `MappedFileRegion`.
Diffstat (limited to 'view/sharedcache/core/MappedFileRegion.h')
| -rw-r--r-- | view/sharedcache/core/MappedFileRegion.h | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/view/sharedcache/core/MappedFileRegion.h b/view/sharedcache/core/MappedFileRegion.h new file mode 100644 index 00000000..7cbf2100 --- /dev/null +++ b/view/sharedcache/core/MappedFileRegion.h @@ -0,0 +1,72 @@ +#pragma once + +#include "binaryninjaapi.h" + +#include <memory> +#include <mutex> +#include <span> +#include <stdint.h> +#include <string> + +class UnmappedAccessException : public std::runtime_error +{ +public: + UnmappedAccessException(uint64_t offset, uint64_t fileLength) + : std::runtime_error(fmt::format("Tried to access offset {:#x} in file of length {:#x}", offset, fileLength)) + {} +}; + +// A memory-mapped region of a file. Owns the mmap for its entire lifetime. +// Data is mapped using MAP_PRIVATE, so modifications to the data are not persisted to disk. +class MappedFileRegion +{ + uint8_t* m_data = nullptr; + size_t m_length = 0; + std::string m_path; + std::once_flag m_slidOnce; + + MappedFileRegion(const MappedFileRegion&) = delete; + MappedFileRegion& operator=(const MappedFileRegion&) = delete; + MappedFileRegion(MappedFileRegion&&) = delete; + MappedFileRegion& operator=(MappedFileRegion&&) = delete; + + struct PrivateTag {}; + +public: + MappedFileRegion(PrivateTag, uint8_t* data, size_t length, std::string path); + ~MappedFileRegion(); + + // Opens file, mmaps with MAP_PRIVATE, closes fd immediately. + // Returns nullptr on failure. + static std::shared_ptr<MappedFileRegion> Open(const std::string& path); + + const std::string& Path() const { return m_path; } + size_t Length() const { return m_length; } + + // Run `fn` exactly once. All callers block until the work is complete. + template <typename F> + void SlideOnce(F&& fn) { std::call_once(m_slidOnce, std::forward<F>(fn)); } + + // Typed reads -- bounds-checked with overflow-safe checks: + // if (sizeof(T) > m_length || offset > m_length - sizeof(T)) + uint8_t ReadUInt8(size_t offset) const; + int8_t ReadInt8(size_t offset) const; + uint16_t ReadUInt16(size_t offset) const; + int16_t ReadInt16(size_t offset) const; + uint32_t ReadUInt32(size_t offset) const; + int32_t ReadInt32(size_t offset) const; + uint64_t ReadUInt64(size_t offset) const; + int64_t ReadInt64(size_t offset) const; + + std::string ReadNullTermString(size_t offset, size_t maxLen = -1) const; + void Read(void* dest, size_t offset, size_t length) const; + BinaryNinja::DataBuffer ReadBuffer(size_t offset, size_t length) const; + std::span<const uint8_t> ReadSpan(size_t offset, size_t length) const; + + // Write. This is not persisted to disk. Used for applying slides only. + void WriteUInt64(size_t offset, uint64_t value); + +private: + template <typename T> + T Read(size_t offset) const; +}; |
