CVE-2026-19138
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/README.md |
modified |
Files Changed
third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/README.mdthird_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.hthird_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized_test.ccthird_party/crashpad/crashpad/snapshot/BUILD.gnthird_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized.ccthird_party/crashpad/README.chromiumthird_party/crashpad/crashpad/third_party/cpp-httplib/README.crashpad
Patch
From c1b901cc861d0d780e911e24d3ec30e42d0d3cee Mon Sep 17 00:00:00 2001 From: Mark Mentovai <[email protected]> Date: Tue, 28 Jul 2026 13:05:04 -0700 Subject: [PATCH] Update Crashpad to ad1827ddbc03f1c214030b31b380243ded660b95 526fd4a0352b Clamp aligned_offset in MemorySnapshotSanitized ece2fa50a313 Fix MemorySnapshotSanitized for 32-bit builds, where size_t ≠ VMAddress ad1827ddbc03 Update cpp-httplib to d66d9a95997d (0.51.0) Bug: 500097298, 539815273 Change-Id: Ibda9f2ea42026623032310b5eb35e90cf453d8d6 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8164782 Commit-Queue: Mark Mentovai <[email protected]> Reviewed-by: Joshua Peraza <[email protected]> Cr-Commit-Position: refs/heads/main@{#1669684} --- diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium index ae571d6..8d2e2fda 100644 --- a/third_party/crashpad/README.chromium +++ b/third_party/crashpad/README.chromium @@ -2,7 +2,7 @@ Short Name: crashpad URL: https://chromium.googlesource.com/crashpad/crashpad Version: N/A -Revision: efdc820b087c20eec9e32cb5e5b1a63dcf73a724 +Revision: ad1827ddbc03f1c214030b31b380243ded660b95 Update Mechanism: Manual License: Apache-2.0 License File: crashpad/LICENSE diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized.cc b/third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized.cc index 58bcdde42..632d010 100644 --- a/third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized.cc +++ b/third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized.cc @@ -16,6 +16,8 @@ #include <string.h> +#include <algorithm> + #include "util/linux/pac_helper.h" namespace crashpad { @@ -55,8 +57,9 @@ static_cast<Pointer>(MemorySnapshotSanitized::kDefaced); // Sanitize up to a word-aligned address. - const size_t aligned_offset = - ((address_ + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1)) - address_; + const size_t aligned_offset = std::min<VMAddress>( + size, + ((address_ + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1)) - address_); memcpy(data, &defaced, aligned_offset); // Sanitize words that aren't small and don't look like pointers. diff --git a/third_party/crashpad/crashpad/third_party/cpp-httplib/README.crashpad b/third_party/crashpad/crashpad/third_party/cpp-httplib/README.crashpad index c1d03f9..24bb49e 100644 --- a/third_party/crashpad/crashpad/third_party/cpp-httplib/README.crashpad +++ b/third_party/crashpad/crashpad/third_party/cpp-httplib/README.crashpad @@ -1,8 +1,8 @@ Name: cpp-httplib Short Name: cpp-httplib URL: https://github.com/yhirose/cpp-httplib -Version: 0.27.0 -Revision: eacc1ca98e5fef25184c7d417e8417225e05e65d +Version: 0.51.0 +Revision: d66d9a95997d51a8ba9822a611d1267757741535 Update Mechanism: Manual License: MIT License File: cpp-httplib/LICENSE @@ -18,13 +18,4 @@ - Patch httplib.h to use #include "third_party/zlib/zlib_crashpad.h" instead of <zlib.h>. - #define CPPHTTPLIB_NO_EXCEPTIONS 1. - - Revert upstream patches that drop support for 32-bit Windows: - - 52163ed9823c Fix #2148 (#2173) - - 9dbaed75efff Fix #2175 (#2177) - - 1f110b54d8b3 Chang #error to #warning for the 32-bit environment check - except 32-bit Windows - - 0b3758ec36be Fix problem with Windows version check - - fbee136dca54 Fix #2193. Allow _WIN32 - - dffce89514c2 Fix 32-bit MSVC compiler error due to unknown command - #warning (#2202) - - Fix sign compare error with sk_X509_OBJECT_num from BoringSSL. + - Remove #pragma comment(lib, …) as this construct is forbidden by Chromium. diff --git a/third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/README.md b/third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/README.md index a69f7be..199505b 100644 --- a/third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/README.md +++ b/third_party/crashpad/crashpad/third_party/cpp-httplib/cpp-httplib/README.md @@ -1,19 +1,29 @@ -cpp-httplib -=========== +# cpp-httplib [](https://github.com/yhirose/cpp-httplib/actions) -A C++11 single-file header-only cross platform HTTP/HTTPS library. +A C++11 single-file header-only cross platform HTTP/HTTPS library.<br> +It's extremely easy to set up. Just include the **[httplib.h](https://raw.githubusercontent.com/yhirose/cpp-httplib/refs/heads/master/httplib.h)** file in your code! -It's extremely easy to set up. Just include the **httplib.h** file in your code! +Learn more in the [official documentation](https://yhirose.github.io/cpp-httplib/) (built with [docs-gen](https://github.com/yhirose/docs-gen)). > [!IMPORTANT] -> This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want. +> This library uses 'blocking' socket I/O. If you are looking for a library with 'non-blocking' socket I/O, this is not the one that you want. Only **HTTP/1.1** is supported — HTTP/2 and HTTP/3 are not implemented. -Simple examples ---------------- +> [!WARNING] +> 32-bit platforms are **NOT supported**. Use at your own risk. The library may compile on 32-bit targets, but no security review has been conducted for 32-bit environments. Integer truncation and other 32-bit-specific issues may exist. **Security reports that only affect 32-bit platforms will be closed without action.** The maintainer does not have access to 32-bit environments for testing or fixing issues. CI includes basic compile checks only, not functional or security testing. -#### Server (Multi-threaded) +## Main Features + +- HTTP Server/Client +- SSL/TLS support (OpenSSL, MbedTLS, wolfSSL) +- [Stream API](README-stream.md) +- [Server-Sent Events](README-sse.md) +- [WebSocket](README-websocket.md) + +## Simple examples + +### Server ```c++ #define CPPHTTPLIB_OPENSSL_SUPPORT @@ -32,7 +42,7 @@ svr.listen("0.0.0.0", 8080); ``` -#### Client +### Client ```c++ #define CPPHTTPLIB_OPENSSL_SUPPORT @@ -44,24 +54,31 @@ // HTTPS httplib::Client cli("https://yhirose.github.io"); -auto res = cli.Get("/hi"); -res->status; -res->body; +if (auto res = cli.Get("/hi")) { + res->status; + res->body; +} ``` -SSL Support ------------ +## SSL/TLS Support -SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked. +cpp-httplib supports multiple TLS backends through an abstraction layer: + +| Backend | Define | Libraries | Notes | +| :------ | :----- | :-------- | :---- | +| OpenSSL | `CPPHTTPLIB_OPENSSL_SUPPORT` | `libssl`, `libcrypto` | [3.0 or later](https://www.openssl.org/policies/releasestrat.html) required | +| Mbed TLS | `CPPHTTPLIB_MBEDTLS_SUPPORT` | `libmbedtls`, `libmbedx509`, `libmbedcrypto` | 2.x, 3.x, and 4.x supported (auto-detected); 4.x renames `libmbedcrypto` to `libtfpsacrypto` | +| wolfSSL | `CPPHTTPLIB_WOLFSSL_SUPPORT` | `libwolfssl` | 5.x supported; must build with `--enable-opensslall` | > [!NOTE] -> cpp-httplib currently supports only version 3.0 or later. Please see [this page](https://www.openssl.org/policies/releasestrat.html) to get more information. +> **Mbed TLS / wolfSSL limitation:** `get_ca_certs()` and `get_ca_names()` only reflect CA certificates loaded via `load_ca_cert_store()`. Certificates loaded through `set_ca_cert_path()` or system certificates (`load_system_certs`) are not enumerable. -> [!TIP] -> For macOS: cpp-httplib now can use system certs with `CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN`. `CoreFoundation` and `Security` should be linked with `-framework`. +> [!NOTE] +> **BoringSSL (best-effort):** BoringSSL builds under `CPPHTTPLIB_OPENSSL_SUPPORT` and is exercised by CI against current upstream. Because BoringSSL does not guarantee API stability, support is best-effort — breakage may occasionally land. Two known behavioral differences vs OpenSSL: (1) BoringSSL's public headers require C++14 or later, so consumers must compile accordingly; (2) hostname verification is SAN-only per RFC 6125 §6.4.4 (no CN fallback). ```c++ -#define CPPHTTPLIB_OPENSSL_SUPPORT +// Use either OpenSSL, Mbed TLS, or wolfSSL +#define CPPHTTPLIB_OPENSSL_SUPPORT // or CPPHTTPLIB_MBEDTLS_SUPPORT or CPPHTTPLIB_WOLFSSL_SUPPORT #include "path/to/httplib.h" // Server @@ -82,15 +99,15 @@ cli.enable_server_hostname_verification(false); ``` -> [!NOTE] -> When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself. - ### SSL Error Handling -When SSL operations fail, cpp-httplib provides detailed error information through two separate error fields: +When SSL operations fail, cpp-httplib provides detailed error information through `ssl_error()` and `ssl_backend_error()`: + +- `ssl_error()` - Returns the TLS-level error code (e.g., `SSL_ERROR_SSL` for OpenSSL) +- `ssl_backend_error()` - Returns the backend-specific error code (e.g., `ERR_get_error()` for OpenSSL/wolfSSL, return value for Mbed TLS) ```c++ -#define CPPHTTPLIB_OPENSSL_SUPPORT +#define CPPHTTPLIB_OPENSSL_SUPPORT // or CPPHTTPLIB_MBEDTLS_SUPPORT or CPPHTTPLIB_WOLFSSL_SUPPORT #include "path/to/httplib.h" httplib::Client cli("https://example.com"); @@ -107,18 +124,18 @@ break; case httplib::Error::SSLLoadingCerts: - std::cout << "SSL cert loading failed, OpenSSL error: " - << std::hex << res.ssl_openssl_error() << std::endl; + std::cout << "SSL cert loading failed, backend error: " + << std::hex << res.ssl_backend_error() << std::endl; break; case httplib::Error::SSLServerVerification: - std::cout << "SSL verification failed, X509 error: " - << res.ssl_openssl_error() << std::endl; + std::cout << "SSL verification failed, verify error: " + << res.ssl_backend_error() << std::endl; break; case httplib::Error::SSLServerHostnameVerification: - std::cout << "SSL hostname verification failed, X509 error: " - << res.ssl_openssl_error() << std::endl; + std::cout << "SSL hostname verification failed, verify error: " + << res.ssl_backend_error() << std::endl; break; default: @@ -127,8 +144,69 @@ } ``` -Server ------- +### Custom Certificate Verification + +You can set a custom verification callback using `tls::VerifyCallback`: + +```c++ +httplib::Client cli("https://example.com"); + +cli.set_server_certificate_verifier( + [](const httplib::tls::VerifyContext &ctx) -> bool { + std::cout << "Subject CN: " << ctx.subject_cn() << std::endl; + std::cout << "Issuer: " << ctx.issuer_name() << std::endl; + std::cout << "Depth: " << ctx.depth << std::endl; + std::cout << "Pre-verified: " << ctx.preverify_ok << std::endl; + + // Inspect SANs (Subject Alternative Names) + for (const auto &san : ctx.sans()) { + std::cout << "SAN: " << san.value << std::endl; + } + + // Return true to accept, false to reject + return ctx.preverify_ok; + }); +``` + +### Peer Certificate Inspection + +On the server side, you can inspect the client's peer certificate from a request handler: + +```c++ +httplib::SSLServer svr("./cert.pem", "./key.pem", + "./client-ca-cert.pem"); + +svr.Get("/", [](const httplib::Request &req, httplib::Response &res) { + auto cert = req.peer_cert(); + if (cert) { + std::cout << "Client CN: " << cert.subject_cn() << std::endl; + std::cout << "Serial: " << cert.serial() << std::endl; + } + + auto sni = req.sni(); + std::cout << "SNI: " << sni << std::endl; +}); +``` + +### Platform-specific Certificate Handling + +cpp-httplib automatically integrates with the OS certificate store on macOS and Windows. This works with all TLS backends. + +| Platform | Behavior | Disable (compile time) | +| :------- | :------- | :--------------------- | +| macOS | Loads system certs from Keychain (link `CoreFoundation` and `Security` with `-framework`). Requires Apple Clang; GCC is not supported for this feature. | `CPPHTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES` | +| Windows | Verifies certs via CryptoAPI (`CertGetCertificateChain` / `CertVerifyCertificateChainPolicy`) with revocation checking | `CPPHTTPLIB_DISABLE_WINDOWS_AUTOMATIC_ROOT_CERTIFICATES_UPDATE` | + +On Windows, verification can also be disabled at runtime: + +```c++ +cli.enable_windows_certificate_verification(false); +``` + +> [!NOTE] +> When using SSL, it seems impossible to avoid SIGPIPE in all cases, since on some operating systems, SIGPIPE can only be suppressed on a per-message basis, but there is no way to make the OpenSSL library do so for its internal communications. If your program needs to avoid being terminated on SIGPIPE, the only fully general way might be to set up a signal handler for SIGPIPE to handle or ignore it yourself. + +## Server ```c++ #include <httplib.h> @@ -164,6 +242,8 @@ if (req.has_param("key")) { auto val = req.get_param_value("key"); } + // Get all values for a given key (e.g., ?tag=a&tag=b) + auto values = req.get_param_values("tag"); res.set_content(req.body, "text/plain"); }); @@ -190,7 +270,7 @@ } ``` -`Post`, `Put`, `Delete` and `Options` methods are also supported. +`Post`, `Put`, `Patch`, `Delete` and `Options` methods are also supported. ### Bind a socket to multiple interfaces and any available port @@ -257,6 +337,11 @@ > [!WARNING] > These static file server methods are not thread-safe. +<!-- --> + +> [!NOTE] +> On POSIX systems, the static file server rejects requests that resolve (via symlinks) to a path outside the mounted base directory. Ensure that the served directory has appropriate permissions, as managing access to the served directory is the application developer's responsibility. + ### File request handler ```cpp @@ -365,6 +450,8 @@ ### Pre request handler +The pre-request handler runs after the route has been matched (so `req.matched_route` and `req.path_params` are available) but **before the request body is read**. This means you can reject a request — for example on a failed authentication or authorization check — without forcing the server to buffer a potentially large body. + ```cpp svr.set_pre_request_handler([](const auto& req, auto& res) { if (req.matched_route == "/user/:user") { @@ -379,6 +466,64 @@ }); ``` +> [!NOTE] +> Because the body has not been read yet, `req.body` and form fields parsed from the body are not available in the pre-request handler. Inspect headers, the path, query parameters, or `req.matched_route` instead. + +### Handler execution order + +`set_start_handler` runs once when the server starts. For each request, handlers run in the following order: + +``` +Request received + │ + ├─ pre_routing_handler route not matched yet, body not read + │ └─ returns Handled → stop here + │ + ├─ file_request_handler (GET/HEAD, static file serving) + │ + ├─ expect_100_continue_handler (when the request has "Expect: 100-continue") + │ + ├─ route matching → req.matched_route is set + │ + ├─ pre_request_handler route matched, body NOT read yet + │ └─ returns Handled → stop here (route handler is skipped) + │ + ├─ route handler Get/Post/...; the request body is read first + │ + └─ post_routing_handler after routing completes + + On a thrown exception → exception_handler + On an error status (4xx/5xx) → error_handler +``` + +Use `pre_routing_handler` to reject a request as early as possible, before the route is known. Use `pre_request_handler` for route-specific checks, since `req.matched_route` is available and the body has not been read yet. + +### Response user data + +`res.user_data` is a type-safe key-value store that lets pre-routing or pre-request handlers pass arbitrary data to route handlers. + +```cpp +struct AuthContext { + std::string user_id; + std::string role; +}; + +svr.set_pre_routing_handler([](const auto& req, auto& res) { + auto token = req.get_header_value("Authorization"); + res.user_data.set("auth", AuthContext{decode_token(token)}); + return Server::HandlerResponse::Unhandled; +}); + +svr.Get("/me", [](const auto& /*req*/, auto& res) { + auto* ctx = res.user_data.get<AuthContext>("auth"); + if (!ctx) { + res.status = StatusCode::Unauthorized_401; + return; + } + res.set_content("Hello " + ctx->user_id, "text/plain"); +}); +``` + ### Form data handling #### URL-encoded form data ('application/x-www-form-urlencoded') ... (file diff truncated)
Regression Test / PoC
diff --git a/third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized_test.cc b/third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized_test.cc
new file mode 100644
index 0000000..2a41397b
--- /dev/null
+++ b/third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized_test.cc
@@ -0,0 +1,115 @@
+// Copyright 2026 The Crashpad Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "snapshot/sanitized/memory_snapshot_sanitized.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <vector>
+
+#include "base/containers/heap_array.h"
+#include "gtest/gtest.h"
+#include "util/misc/range_set.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+constexpr uint8_t kFillByte = 0x55;
+
+class BufferMemorySnapshot final : public MemorySnapshot {
+ public:
+ BufferMemorySnapshot(uint64_t address, size_t size)
+ : address_(address), size_(size) {}
+
+ BufferMemorySnapshot(const BufferMemorySnapshot&) = delete;
+ BufferMemorySnapshot& operator=(const BufferMemorySnapshot&) = delete;
+
+ uint64_t Address() const override { return address_; }
+ size_t Size() const override { return size_; }
+
+ bool Read(Delegate* delegate) const override {
+ if (size_ == 0) {
+ return delegate->MemorySnapshotDelegateRead(nullptr, 0);
+ }
+ auto buffer = base::HeapArray<uint8_t>::Uninit(size_);
+ memset(buffer.data(), kFillByte, buffer.size());
+ return delegate->MemorySnapshotDelegateRead(buffer.data(), buffer.size());
+ }
+
+ const MemorySnapshot* MergeWithOtherSnapshot(
+ const MemorySnapshot*) const override {
+ return nullptr;
+ }
+
+ private:
+ uint64_t address_;
+ size_t size_;
+};
+
+class CapturingDelegate : public MemorySnapshot::Delegate {
+ public:
+ bool MemorySnapshotDelegateRead(void* data, size_t size) override {
+ captured_.assign(static_cast<uint8_t*>(data),
+ static_cast<uint8_t*>(data) + size);
+ return true;
+ }
+
+ const std::vector<uint8_t>& captured() const { return captured_; }
+
+ private:
+ std::vector<uint8_t> captured_;
+};
+
+void ExpectSanitizedShortUnalignedRegion(uint64_t address,
+ size_t size,
+ bool is_64_bit) {
+ BufferMemorySnapshot wrapped(address, size);
+ RangeSet ranges;
+ internal::MemorySnapshotSanitized sanitized(&wrapped, &ranges, is_64_bit);
+
+ CapturingDelegate delegate;
+ ASSERT_TRUE(sanitized.Read(&delegate));
+ ASSERT_EQ(delegate.captured().size(), size);
+
+ // The region is shorter than a word and contains no pointer-aligned word, so
+ // every byte should have been defaced.
+ for (size_t index = 0; index < size; ++index) {
+ EXPECT_NE(delegate.captured()[index], kFillByte)
+ << "address=" << address << " size=" << size << " index=" << index;
+ }
+}
+
+TEST(MemorySnapshotSanitized, ShortUnalignedRegion64) {
+ for (uint64_t offset = 1; offset < sizeof(uint64_t); ++offset) {
+ for (size_t size = 1; size < sizeof(uint64_t); ++size) {
+ ExpectSanitizedShortUnalignedRegion(
+ 0x1000 + offset, size, /*is_64_bit=*/true);
+ }
+ }
+}
+
+TEST(MemorySnapshotSanitized, ShortUnalignedRegion32) {
+ for (uint64_t offset = 1; offset < sizeof(uint32_t); ++offset) {
+ for (size_t size = 1; size < sizeof(uint32_t); ++size) {
+ ExpectSanitizedShortUnalignedRegion(
+ 0x1000 + offset, size, /*is_64_bit=*/false);
+ }
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
Original Bug Report
Heap OOB Write and Integer Underflow in Crashpad MemorySanitizer
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 security team.
Overview: A compromised renderer can trigger a heap out-of-bounds write and massive memory corruption in the unsandboxed chrome_crashpad_handler by requesting a stack-sanitized crash dump. By manipulating a thread’s Stack Pointer and TLS registers, the attacker forces an integer underflow during buffer sanitization. While this heavily corrupts memory and guarantees an eventual crash, exploiting a race condition could theoretically lead to a sandbox escape.
Affected files:
third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized.ccthird_party/crashpad/crashpad/snapshot/memory_snapshot_generic.h
Estimated timestamp from git blame: 2023-04-10
Summary
A potential heap out-of-bounds (OOB) write and integer underflow exists in the stack sanitization logic of Crashpad. The vulnerability occurs when the MemorySanitizer processes a highly specific, attacker-controlled stack memory region that is very small (e.g., 1 byte) and unaligned. This can be triggered by a compromised renderer process, potentially leading to a sandbox escape via memory corruption in the privileged chrome_crashpad_handler process.
Root Cause Analysis
The vulnerability is located in third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized.cc within the MemorySanitizer::Sanitize template function:
// Sanitize up to a word-aligned address.
const size_t aligned_offset =
((address_ + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1)) - address_;
memcpy(data, &defaced, aligned_offset);
// Sanitize words that aren't small and don't look like pointers.
size_t word_count = (size - aligned_offset) / sizeof(Pointer);
The aligned_offset is calculated as the distance from the region’s base address_ to the next word-aligned boundary. For a 64-bit architecture, this can be up to 7 bytes.
The function crucially assumes that size >= aligned_offset. If an attacker can force a situation where size < aligned_offset, two critical memory corruptions occur:
- Heap OOB Write: The
memcpywritesaligned_offsetbytes (e.g., 7) of thedefacedconstant intodata. If the underlying heap buffer is onlysizebytes (e.g., 1 byte), this overflows the buffer. - Integer Underflow: The calculation
(size - aligned_offset)uses unsigned arithmetic (size_t). Whensize < aligned_offset, this underflows to a massive value (e.g.,~0x1FFFFFFFFFFFFFFE). The subsequent loop iterates over this massiveword_count, blindly overwriting vast swathes of the handler’s heap memory with0x0defaced0defaceduntil the process hits an unmapped page and crashes.
Potential Attack Steps
Note: These are suggested steps; we do not currently have a working proof of concept to run this exploit end-to-end.
- A compromised renderer process creates a new thread.
- The attacker manipulates the thread’s registers to craft an invalid stack layout:
- Sets the Stack Pointer (SP) to an unaligned memory address
X, whereX % 8 == 1. - Sets the Thread Local Storage (TLS) register to
X + 1.
- Sets the Stack Pointer (SP) to an unaligned memory address
- The attacker creates a
SanitizationInformationstruct in its memory and setssanitize_stacks = true. - The attacker sends a
kTypeCrashDumpRequestIPC to the unsandboxedchrome_crashpad_handler, pointing to this struct. - The handler’s
ProcessReaderLinux::Thread::InitializeStackFromSPlogic calculates the thread’s stack size astls_address - stack_region_address. Due to the attacker’s setup (X + 1minusX), the stack size is evaluated as1byte. - The handler allocates a 1-byte heap buffer for the stack snapshot and passes it to
MemorySanitizer::Sanitize. Sanitizeevaluatesaligned_offsetas7, triggering the out-of-boundsmemcpyand the integer underflow.- The massive memory corruption loop races against other handler threads (like the upload thread). If the attacker can win a race condition using the corrupted heap state before the inevitable segmentation fault, this could result in arbitrary code execution (sandbox escape).
Suggested Fix
Add bounds checking in MemorySanitizer::Sanitize to ensure that aligned_offset does not exceed the buffer size.
// Sanitize up to a word-aligned address, bounded by the region size.
const size_t aligned_offset = std::min(
size,
((address_ + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1)) - address_);
memcpy(data, &defaced, aligned_offset);
// Sanitize words that aren't small and don't look like pointers.
size_t word_count = (size - aligned_offset) / sizeof(Pointer);
Evaluated with Chrome root at commit: f200f57a19490707ff8bc7aa5de3cbc443a3afad
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.