Detect OOM when memory mapping fails in MappedFileReader/MappedFileWriter When CreateFileMapping/MapViewOfFile (Windows) or mmap (POSIX) fail due to memory pressure, the error was reported as a generic "Can't map file to memory" with no way to distinguish OOM from other mapping failures. Add an is_oom() method to both MappedFileReader and MappedFileWriter that checks GetLastError() (ERROR_NOT_ENOUGH_MEMORY, ERROR_COMMITMENT_LIMIT) or errno (ENOMEM) immediately after the mapping failure. This allows the caller (moz_zucchini.cc) to report a specific out-of-memory status code instead of a generic read/write error. --- base/files/memory_mapped_file.h | 8 +++++++- base/files/memory_mapped_file_posix.cc | 4 ++++ base/files/memory_mapped_file_win.cc | 20 +++++++++++++++++++ components/zucchini/mapped_file.cc | 6 ++++++ components/zucchini/mapped_file.h | 12 +++++++++++ 5 files changed, 49 insertions(+), 1 deletion(-) diff --git a/third_party/zucchini/chromium/base/files/memory_mapped_file.h b/third_party/zucchini/chromium/base/files/memory_mapped_file.h index 44ee14dec8f5..767f9db2a269 100644 --- a/third_party/zucchini/chromium/base/files/memory_mapped_file.h +++ b/third_party/zucchini/chromium/base/files/memory_mapped_file.h @@ -115,10 +115,12 @@ class BASE_EXPORT MemoryMappedFile { bool IsValid() const; #if defined(MOZ_ZUCCHINI) + bool is_mapping_oom() const { return is_mapping_oom_; } + // Flushes memory-mapped changes to disk. Returns true on success, false on // error. Must be called before unmapping if changes need to be persisted. bool Flush(); -#endif // MOZ_ZUCCHINI +#endif // defined(MOZ_ZUCCHINI) private: // Given the arbitrarily aligned memory region [start, size], returns the @@ -154,6 +156,10 @@ class BASE_EXPORT MemoryMappedFile { #if BUILDFLAG(IS_WIN) win::ScopedHandle file_mapping_; #endif + +#if defined(MOZ_ZUCCHINI) + bool is_mapping_oom_ = false; +#endif // defined(MOZ_ZUCCHINI) }; } // namespace base diff --git a/third_party/zucchini/chromium/base/files/memory_mapped_file_posix.cc b/third_party/zucchini/chromium/base/files/memory_mapped_file_posix.cc index e6f624cc9eaa..0c2cc1ac3e1c 100644 --- a/third_party/zucchini/chromium/base/files/memory_mapped_file_posix.cc +++ b/third_party/zucchini/chromium/base/files/memory_mapped_file_posix.cc @@ -4,6 +4,9 @@ #include "base/files/memory_mapped_file.h" +#if defined(MOZ_ZUCCHINI) +#include +#endif // defined(MOZ_ZUCCHINI) #include #include #include @@ -92,6 +95,7 @@ bool MemoryMappedFile::MapFileRegionToMemory( data_ = data; } else { + is_mapping_oom_ = errno == ENOMEM; #else data_ = static_cast(mmap(nullptr, map_size, flags, MAP_SHARED, file_.GetPlatformFile(), map_start)); diff --git a/third_party/zucchini/chromium/base/files/memory_mapped_file_win.cc b/third_party/zucchini/chromium/base/files/memory_mapped_file_win.cc index 777b32358f86..ad887e2d216b 100644 --- a/third_party/zucchini/chromium/base/files/memory_mapped_file_win.cc +++ b/third_party/zucchini/chromium/base/files/memory_mapped_file_win.cc @@ -89,8 +89,18 @@ bool MemoryMappedFile::MapFileRegionToMemory( file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), NULL, flags, size.HighPart, size.LowPart, NULL)); +#if defined(MOZ_ZUCCHINI) + if (!file_mapping_.is_valid()) { + DWORD last_error = ::GetLastError(); + is_mapping_oom_ = last_error == ERROR_NOT_ENOUGH_MEMORY || + last_error == ERROR_OUTOFMEMORY || + last_error == ERROR_COMMITMENT_LIMIT; + return false; + } +#else if (!file_mapping_.is_valid()) return false; +#endif // defined(MOZ_ZUCCHINI) ULARGE_INTEGER map_start = {}; SIZE_T map_size = 0; @@ -131,8 +141,18 @@ bool MemoryMappedFile::MapFileRegionToMemory( ::MapViewOfFile(file_mapping_.get(), (flags & PAGE_READONLY) ? FILE_MAP_READ : FILE_MAP_WRITE, map_start.HighPart, map_start.LowPart, map_size)); +#if defined(MOZ_ZUCCHINI) + if (data_ == nullptr) { + DWORD last_error = ::GetLastError(); + is_mapping_oom_ = last_error == ERROR_NOT_ENOUGH_MEMORY || + last_error == ERROR_OUTOFMEMORY || + last_error == ERROR_COMMITMENT_LIMIT; + return false; + } +#else if (data_ == nullptr) return false; +#endif // defined(MOZ_ZUCCHINI) data_ += data_offset; return true; } diff --git a/third_party/zucchini/chromium/components/zucchini/mapped_file.cc b/third_party/zucchini/chromium/components/zucchini/mapped_file.cc index 0e786aabf15f..0563877873ad 100644 --- a/third_party/zucchini/chromium/components/zucchini/mapped_file.cc +++ b/third_party/zucchini/chromium/components/zucchini/mapped_file.cc @@ -18,6 +18,9 @@ MappedFileReader::MappedFileReader(base::File file) { } if (!buffer_.Initialize(std::move(file))) { error_ = "Can't map file to memory."; +#if defined(MOZ_ZUCCHINI) + error_is_oom_ = buffer_.is_mapping_oom(); +#endif // defined(MOZ_ZUCCHINI) } } @@ -56,6 +59,9 @@ MappedFileWriter::MappedFileWriter(const base::FilePath& file_path, base::MemoryMappedFile::READ_WRITE_EXTEND); if (!is_ok) { error_ = "Can't map file to memory."; +#if defined(MOZ_ZUCCHINI) + error_is_oom_ = buffer_.is_mapping_oom(); +#endif // defined(MOZ_ZUCCHINI) } } diff --git a/third_party/zucchini/chromium/components/zucchini/mapped_file.h b/third_party/zucchini/chromium/components/zucchini/mapped_file.h index ed7d0f97217b..f6927dc15b0e 100644 --- a/third_party/zucchini/chromium/components/zucchini/mapped_file.h +++ b/third_party/zucchini/chromium/components/zucchini/mapped_file.h @@ -32,10 +32,16 @@ class MappedFileReader { bool HasError() { return !error_.empty() || !buffer_.IsValid(); } const std::string& error() { return error_; } +#if defined(MOZ_ZUCCHINI) + bool error_is_oom() const { return error_is_oom_; } +#endif // defined(MOZ_ZUCCHINI) private: std::string error_; base::MemoryMappedFile buffer_; +#if defined(MOZ_ZUCCHINI) + bool error_is_oom_ = false; +#endif // defined(MOZ_ZUCCHINI) }; // A file writer wrapper. The target file is deleted on destruction unless @@ -69,6 +75,9 @@ class MappedFileWriter { bool HasError() { return !error_.empty() || !buffer_.IsValid(); } const std::string& error() { return error_; } +#if defined(MOZ_ZUCCHINI) + bool error_is_oom() const { return error_is_oom_; } +#endif // defined(MOZ_ZUCCHINI) // Indicates that the file should not be deleted on destruction. Returns true // iff the operation succeeds. @@ -92,6 +101,9 @@ class MappedFileWriter { base::File file_handle_; base::MemoryMappedFile buffer_; OnCloseDeleteBehavior delete_behavior_; +#if defined(MOZ_ZUCCHINI) + bool error_is_oom_ = false; +#endif // defined(MOZ_ZUCCHINI) }; } // namespace zucchini -- 2.49.0.windows.1