Chrome · Safebrowsing
CVE-2026-17950
Logic Error in Safebrowsing
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
MachOFeatureExtractorchrome/utility/safe_browsing/mac/dmg_analyzer.cc |
modified | |
buffer_chrome/utility/safe_browsing/mac/dmg_analyzer.cc |
modified | |
ifchrome/utility/safe_browsing/mac/dmg_analyzer.cc |
modified |
Files Changed
chrome/utility/safe_browsing/mac/dmg_analyzer.cc
Patch
From 4db8d3e6e418c8dadf477c605074e0a225693876 Mon Sep 17 00:00:00 2001 From: Lily Chen <[email protected]> Date: Mon, 08 Jun 2026 07:00:18 -0700 Subject: [PATCH] DMG analyzer: Refactor MachOFeatureExtractor to clarify API MachOFeatureExtractor is refactored to take ownership of the ReadStream it consumes, which clarifies the API and makes it harder to make errors like crbug.com/517063658. This is just a cleanup to follow-up on crrev.com/c/7895850, which fixed the actual bug. Bug: 517063658 Change-Id: Id65cd07f46d5d2c8ec758a716906889d6a6a6964 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7905440 Auto-Submit: Lily Chen <[email protected]> Commit-Queue: thefrog <[email protected]> Reviewed-by: thefrog <[email protected]> Cr-Commit-Position: refs/heads/main@{#1643164} --- diff --git a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc index 79747370..894f4f8 100644 --- a/chrome/utility/safe_browsing/mac/dmg_analyzer.cc +++ b/chrome/utility/safe_browsing/mac/dmg_analyzer.cc @@ -36,87 +36,98 @@ // image. In addition, this class will compute the SHA256 hash of the file. class MachOFeatureExtractor { public: - MachOFeatureExtractor(); + explicit MachOFeatureExtractor(std::unique_ptr<ReadStream> stream); MachOFeatureExtractor(const MachOFeatureExtractor&) = delete; MachOFeatureExtractor& operator=(const MachOFeatureExtractor&) = delete; ~MachOFeatureExtractor(); - // Tests if the stream references a Mach-O image by examinig its magic - // number. - bool IsMachO(ReadStream* stream); + // Returns whether the stream references a Mach-O image by examining its magic + // number. If not, the stream is reset and it is an error to attempt further + // operations. + bool IsMachO(); - // Computes the hash of the data in |stream| and extracts the Mach-O - // features from the data. Returns true if successful, or false on error or - // if the file was not Mach-O. - bool ExtractFeatures(ReadStream* stream, - ClientDownloadRequest_ArchivedBinary* result); + // Computes the hash of the data in the underlying stream and extracts the + // Mach-O features from the data, populating `result` with the features. + // Returns true on success, or false on error. + bool ExtractFeatures(ClientDownloadRequest_ArchivedBinary* result); private: - // Reads the entire stream and updates the hash. - bool HashAndCopyStream(ReadStream* stream, - base::span<uint8_t, crypto::hash::kSha256Size> digest); + // Reads the entire stream into `buffer` and updates `digest` with the hash. + bool HashAndCopyStream(base::span<uint8_t, crypto::hash::kSha256Size> digest, + std::vector<uint8_t>& buffer); scoped_refptr<BinaryFeatureExtractor> bfe_; - std::vector<uint8_t> buffer_; // Buffer that contains read stream data. + std::unique_ptr<ReadStream> stream_; }; -MachOFeatureExtractor::MachOFeatureExtractor() - : bfe_(new BinaryFeatureExtractor()), - buffer_() { - buffer_.reserve(1024 * 1024); -} +MachOFeatureExtractor::MachOFeatureExtractor(std::unique_ptr<ReadStream> stream) + : bfe_(base::MakeRefCounted<BinaryFeatureExtractor>()), + stream_(std::move(stream)) {} MachOFeatureExtractor::~MachOFeatureExtractor() = default; -bool MachOFeatureExtractor::IsMachO(ReadStream* stream) { +bool MachOFeatureExtractor::IsMachO() { + if (!stream_) { + return false; + } uint32_t magic = 0; - return stream->ReadType<uint32_t>(magic) && - MachOImageReader::IsMachOMagicValue(magic); + bool is_mach_o = stream_->ReadType<uint32_t>(magic) && + MachOImageReader::IsMachOMagicValue(magic); + if (!is_mach_o) { + stream_.reset(); + } + return is_mach_o; } bool MachOFeatureExtractor::ExtractFeatures( - ReadStream* stream, ClientDownloadRequest_ArchivedBinary* result) { + if (!stream_) { + return false; + } std::array<uint8_t, crypto::hash::kSha256Size> hash; - if (!HashAndCopyStream(stream, hash)) { + std::vector<uint8_t> buffer; + if (!HashAndCopyStream(hash, buffer)) { return false; } if (!bfe_->ExtractImageFeaturesFromData( - buffer_, 0, result->mutable_image_headers(), + buffer, 0, result->mutable_image_headers(), result->mutable_signature()->mutable_signed_data())) { return false; } - result->set_length(buffer_.size()); + result->set_length(buffer.size()); result->mutable_digests()->set_sha256(base::as_string_view(hash)); return true; } bool MachOFeatureExtractor::HashAndCopyStream( - ReadStream* stream, - base::span<uint8_t, crypto::hash::kSha256Size> hash) { - if (stream->Seek(0, SEEK_SET) != 0) + base::span<uint8_t, crypto::hash::kSha256Size> hash, + std::vector<uint8_t>& buffer) { + if (stream_->Seek(0, SEEK_SET) != 0) { return false; + } - buffer_.clear(); + buffer.clear(); + buffer.reserve(1024 * 1024); crypto::hash::Hasher hasher(crypto::hash::HashKind::kSha256); size_t bytes_read; - const size_t kBufferSize = 2048; + const size_t kBufferSizeIncrement = 2048; do { - size_t buffer_offset = buffer_.size(); + size_t buffer_offset = buffer.size(); - buffer_.resize(buffer_.size() + kBufferSize); - base::span<uint8_t> read_buf = base::span(buffer_).last(kBufferSize); - if (!stream->Read(read_buf, &bytes_read)) { + buffer.resize(buffer.size() + kBufferSizeIncrement); + base::span<uint8_t> read_buf = + base::span(buffer).last(kBufferSizeIncrement); + if (!stream_->Read(read_buf, &bytes_read)) { return false; } - buffer_.resize(buffer_offset + bytes_read); + buffer.resize(buffer_offset + bytes_read); read_buf = read_buf.first(bytes_read); if (bytes_read) { hasher.Update(read_buf); @@ -148,7 +159,6 @@ } bool DMGAnalyzer::ResumeExtraction() { - MachOFeatureExtractor feature_extractor; while (iterator_->Next()) { std::unique_ptr<ReadStream> stream = iterator_->GetReadStream(); if (!stream) { @@ -184,12 +194,15 @@ detached_signature->set_file_name(path); detached_signature->set_contents(signature_contents.data(), signature_contents.size()); - } else if (feature_extractor.IsMachO(stream.get())) { + continue; + } + + MachOFeatureExtractor feature_extractor(std::move(stream)); + if (feature_extractor.IsMachO()) { ClientDownloadRequest_ArchivedBinary* binary = results()->archived_binary.Add(); binary->set_file_path(path); - - if (feature_extractor.ExtractFeatures(stream.get(), binary)) { + if (feature_extractor.ExtractFeatures(binary)) { binary->set_download_type( ClientDownloadRequest_DownloadType_MAC_EXECUTABLE); binary->set_is_executable(true); @@ -198,7 +211,7 @@ results()->archived_binary.RemoveLast(); } } else { - // Get a new `stream` because it was read from in previous branches. + // Get a new `stream` because it was moved from in previous branches. stream = iterator_->GetReadStream(); DownloadFileType_InspectionType file_type = GetFileType(base::FilePath(path));
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page