Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactHeap buffer overflow in Extensions
DescriptionHeap buffer overflow in Extensions
ComponentExtensions
Bug ClassOOB
Tracker501674219
Fix commit6c2a55c47054 (chromium/src) +16/-8
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Files Changed

  • components/storage_monitor/storage_monitor_linux.cc
From 6c2a55c4705423b3fa331e52ffb6b4dee666877b Mon Sep 17 00:00:00 2001
From: Lei Zhang <thestig@chromium.org>
Date: Wed, 15 Apr 2026 13:20:30 -0700
Subject: [PATCH] Keep StorageMonitorLinux maps in sync

When StorageMonitorLinux::EjectDevice() gets called, the
`mount_info_map_` and `mount_priority_map_` can get out of sync. Fix
this using similar logic as UpdateMtab().

Along the way, remove an unused parameter in
EjectPathOnBlockingTaskRunner().

Bug: 501674219
Change-Id: I2cb82f55a463332229fd8c49f7973dbc7179b7df
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7763903
Reviewed-by: Thomas Anderson <thomasanderson@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1615367}
---

diff --git a/components/storage_monitor/storage_monitor_linux.cc b/components/storage_monitor/storage_monitor_linux.cc
index 87d267c..88ca1c4 100644
--- a/components/storage_monitor/storage_monitor_linux.cc
+++ b/components/storage_monitor/storage_monitor_linux.cc
@@ -176,8 +176,7 @@
 }
 
 StorageMonitor::EjectStatus EjectPathOnBlockingTaskRunner(
-    const base::FilePath& path,
-    const base::FilePath& device) {
+    const base::FilePath& path) {
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::MAY_BLOCK);
 
@@ -274,19 +273,28 @@
   }
 
   // Find the mount point for the given device ID.
-  base::FilePath path;
-  base::FilePath device;
+  base::FilePath mount_point;
   for (auto it = mount_info_map_.begin(); it != mount_info_map_.end(); ++it) {
     const MountPointInfo& mount_point_info = it->second;
     if (mount_point_info.storage_info.device_id() == device_id) {
-      path = it->first;
-      device = mount_point_info.mount_device;
+      mount_point = it->first;
+      const base::FilePath& mount_device = mount_point_info.mount_device;
+      auto priority_it = mount_priority_map_.find(mount_device);
+      CHECK(priority_it != mount_priority_map_.end());
+      ReferencedMountPoint& priority_mount_point = priority_it->second;
+      bool erased = priority_mount_point.erase(mount_point);
+      CHECK(erased);
+      if (priority_mount_point.empty()) {
+        mount_priority_map_.erase(priority_it);
+      }
+
+      // Do this at the end so `mount_device` is valid while accessed.
       mount_info_map_.erase(it);
       break;
     }
   }
 
-  if (path.empty()) {
+  if (mount_point.empty()) {
     std::move(callback).Run(EJECT_NO_SUCH_DEVICE);
     return;
   }
@@ -295,7 +303,7 @@
 
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
-      base::BindOnce(&EjectPathOnBlockingTaskRunner, path, device),
+      base::BindOnce(&EjectPathOnBlockingTaskRunner, mount_point),
       std::move(callback));
 }
 
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential OOB heap read in StorageMonitorLinux via EjectDevice map desync

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team.

Overview: A desynchronization between internal tracking maps in StorageMonitorLinux::EjectDevice can lead to an out-of-bounds heap read when a busy device fails to unmount. The code attempts to copy a previously erased mount entry using an invalid std::map::end() iterator. If an attacker controls adjacent heap memory, this out-of-bounds copy can potentially be leveraged into an arbitrary memory disclosure.

Affected files:

  • components/storage_monitor/storage_monitor_linux.cc
  • components/storage_monitor/storage_monitor_linux.h

Estimated timestamp from git blame: 2013-04-30

Description

StorageMonitorLinux maintains two parallel maps to track storage devices: mount_info_map_ (maps mount paths to device info) and mount_priority_map_ (maps device paths to mount paths). A potential out-of-bounds (OOB) heap read exists due to a map desynchronization when ejecting a device.

When a device is ejected via chrome.system.storage.ejectDevice, StorageMonitorLinux::EjectDevice correctly removes the device’s entry from mount_info_map_ but fails to remove the corresponding entry from mount_priority_map_. If the background /bin/umount operation fails (e.g., because the filesystem is busy), the device remains in the system’s /etc/mtab.

During the next mtab update (StorageMonitorLinux::UpdateMtab), the code detects the device in the mtab but finds it missing from mount_info_map_. It then calls IsDeviceAlreadyMounted, which checks the stale mount_priority_map_ and incorrectly returns true. Believing the device was re-mounted, the code calls HandleDeviceMountedMultipleTimes.

Inside HandleDeviceMountedMultipleTimes, the code fetches the stale mount path from the priority map and executes:

mount_info_map_[mount_point] = mount_info_map_.find(other_mount_point)->second;

Because other_mount_point was already erased, find() returns mount_info_map_.end(). This iterator is unconditionally dereferenced. In Chromium’s libc++, end() points to an internal header node (__tree_end_node) embedded within the StorageMonitorLinux object.

The resulting copy assignment reads 152 bytes of overlapping object data (the size of MountPointInfo), spilling past the bounds of mount_info_map_ over subsequent class members (mtab_watcher_task_runner_, sequence_checker_, etc.) and into the adjacent heap chunk.

Because MountPointInfo contains multiple std::string objects (24 bytes each), an attacker who can manipulate the adjacent heap layout can forge a std::string object (by setting the LSB to 1 to indicate a “long string” and supplying a target pointer). The std::string copy constructor will interpret this OOB data and perform a wild memory read from the target pointer. The leaked data is then stored in the new mount_info_map_ entry and can be retrieved via the chrome.system.storage.getInfo API.

Potential Reproduction Steps

Note: These are suggested steps based on code analysis; our tooling agent does not have the ability to run live exploit code to verify a full chain.

  1. On Linux, install an extension with the system.storage permission.
  2. Insert a removable USB device.
  3. Using the extension or a browser tab, open a file on the mount point to ensure the filesystem is kept busy.
  4. Call chrome.system.storage.ejectDevice() targeting the USB device.
  5. Wait for or trigger a system /etc/mtab update (e.g., by mounting a different benign device).
  6. The browser process hits the stale entry and performs the out-of-bounds copy. If the heap was successfully groomed beforehand, calling chrome.system.storage.getInfo() will expose the leaked browser memory.

Suggested Fix

In StorageMonitorLinux::EjectDevice, ensure that when an entry is erased from mount_info_map_, it is also correctly removed from mount_priority_map_:

// components/storage_monitor/storage_monitor_linux.cc:284
mount_info_map_.erase(mount_info);
auto priority_iter = mount_priority_map_.find(device);
if (priority_iter != mount_priority_map_.end()) {
  priority_iter->second.erase(path);
  if (priority_iter->second.empty()) {
    mount_priority_map_.erase(priority_iter);
  }
}

Additionally, add a bounds check in HandleDeviceMountedMultipleTimes to prevent end() dereferences:

auto it = mount_info_map_.find(other_mount_point);
CHECK(it != mount_info_map_.end());
mount_info_map_[mount_point] = it->second;

Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

Raised in root component due to access or custom field issues on 1456107

View on issue tracker