Add PartitionAlloc support

Fix a crash on Mac.

The crash is in partition_alloc::internal::PartitionFreelistEntry::CheckFreeListForThreadCache()
called from partition_alloc::ThreadCache::PurgeCurrentThread().

The direct cause of the crash is an uninitialized ThreadCache being
accessed. The root cause is that the required initialization
routines are accidentally removed by the linker and not called.

This could happen because our code path related to PartitionAlloc
is an uncommon one and differs from the standard procedures used
by the browser processes.

Therefore fix the crash by adopting much of the PartitionAlloc
initialization procedures used by the browser processes.

PA config differences from the full browser:

* No PCScan
* No Backup Ref Ptr checks
* No RawPtr checks

New behavior enabled by this change:

* Thread Cache: very small performance gain because allocations are few,
  but this code path is more mainstream thus more likely to be well tested.
This commit is contained in:
klzgrad 2022-11-05 18:34:44 +08:00
parent 611b3bdcf9
commit 6e5b245e64
6 changed files with 234 additions and 49 deletions

View File

@ -103,9 +103,6 @@ jobs:
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '^1.18.1'
- name: Cache toolchains (Linux, OpenWrt, Android)
uses: actions/cache@v3
with:
@ -125,12 +122,12 @@ jobs:
path: src/out/sysroot-build/bullseye/bullseye_*
key: sysroot-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "::set-output name=date::$(date +%s)"
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v3
with:
path: ~/.ccache
key: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
key: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-linux-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- name: Install APT packages
run: |
@ -173,9 +170,6 @@ jobs:
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '^1.18.1'
- name: Cache toolchains (Linux, OpenWrt, Android)
uses: actions/cache@v3
with:
@ -200,12 +194,12 @@ jobs:
path: src/out/sysroot-build/android/
key: sysroot-android-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "::set-output name=date::$(date +%s)"
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v3
with:
path: ~/.ccache
key: ccache-android-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
key: ccache-android-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-android-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- name: Install APT packages
run: |
@ -249,9 +243,6 @@ jobs:
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '^1.18.1'
- name: Cache toolchains
uses: actions/cache@v3
with:
@ -274,12 +265,12 @@ jobs:
path: src/chrome/build/pgo_profiles/chrome-win32-*
key: pgo-win32-arm64-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "::set-output name=date::$(date +%s)"
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v3
with:
path: ~/AppData/Local/Mozilla/sccache
key: ccache-win-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
key: ccache-win-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-win-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- run: ./get-clang.sh
- run: ~/.cargo/bin/sccache -z
@ -316,9 +307,6 @@ jobs:
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '^1.18.1'
- name: Cache toolchains and PGO
uses: actions/cache@v3
with:
@ -328,12 +316,12 @@ jobs:
src/gn/
key: toolchains-pgo-mac-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "::set-output name=date::$(date +%s)"
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v3
with:
path: ~/Library/Caches/ccache
key: ccache-mac-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
key: ccache-mac-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-mac-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- run: brew install ninja ccache
- run: ./get-clang.sh
@ -365,7 +353,7 @@ jobs:
strategy:
fail-fast: false
matrix:
arch: [x64, arm64]
arch: [arm64]
env:
EXTRA_FLAGS: 'target_cpu="${{ matrix.arch }}" target_os="ios" ios_enable_code_signing=false'
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
@ -380,12 +368,12 @@ jobs:
src/gn/
key: toolchains-pgo-mac-${{ hashFiles('CHROMIUM_VERSION') }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "::set-output name=date::$(date +%s)"
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v3
with:
path: ~/Library/Caches/ccache
key: ccache-ios-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
key: ccache-ios-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-ios-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- run: brew install ninja ccache
- run: ./get-clang.sh
@ -498,9 +486,6 @@ jobs:
BUNDLE: naiveproxy-${{ github.event.release.tag_name }}-${{ github.job }}-${{ matrix.arch }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: '^1.18.1'
- name: Cache toolchains (Linux, OpenWrt, Android)
uses: actions/cache@v3
with:
@ -520,12 +505,12 @@ jobs:
path: src/out/sysroot-build/openwrt
key: sysroot-openwrt-22.03.0-${{ matrix.arch }}-v${{ env.CACHE_EPOCH }}
- id: ccache-timestamp
run: echo "::set-output name=date::$(date +%s)"
run: echo "CCACHE_TIMESTAMP=$(date +%s)" >>$GITHUB_OUTPUT
- name: Cache ccache files
uses: actions/cache@v3
with:
path: ~/.ccache
key: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.date }}
key: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-${{ steps.ccache-timestamp.outputs.CCACHE_TIMESTAMP }}
restore-keys: ccache-openwrt-${{ matrix.arch }}-${{ hashFiles('CHROMIUM_VERSION') }}-
- name: Install APT packages
run: |

View File

@ -1745,6 +1745,8 @@ executable("naive") {
"tools/naive/redirect_resolver.cc",
"tools/naive/socks5_server_socket.cc",
"tools/naive/socks5_server_socket.h",
"tools/naive/partition_alloc_support.cc",
"tools/naive/partition_alloc_support.h",
]
deps = [
@ -1754,4 +1756,8 @@ executable("naive") {
"//components/version_info:version_info",
"//url",
]
if (is_apple) {
deps += [ "//base/allocator:early_zone_registration_mac" ]
}
}

View File

@ -15,6 +15,7 @@
#include "base/strings/strcat.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
@ -30,7 +31,7 @@
#include "net/tools/naive/socks5_server_socket.h"
#include "url/scheme_host_port.h"
#if defined(OS_LINUX)
#if BUILDFLAG(IS_LINUX)
#include <linux/netfilter_ipv4.h>
#include <netinet/in.h>
#include <sys/socket.h>
@ -217,7 +218,7 @@ int NaiveConnection::DoConnectServer() {
static_cast<const HttpProxySocket*>(client_socket_.get());
origin = socket->request_endpoint();
} else if (protocol_ == ClientProtocol::kRedir) {
#if defined(OS_LINUX)
#if BUILDFLAG(IS_LINUX)
const auto* socket =
static_cast<const TCPClientSocket*>(client_socket_.get());
IPEndPoint peer_endpoint;

View File

@ -67,8 +67,9 @@
#include "url/gurl.h"
#include "url/scheme_host_port.h"
#include "url/url_util.h"
#include "net/tools/naive/partition_alloc_support.h"
#if defined(OS_MACOSX)
#if BUILDFLAG(IS_APPLE)
#include "base/mac/scoped_nsautorelease_pool.h"
#endif
@ -246,7 +247,7 @@ bool ParseCommandLine(const CommandLine& cmdline, Params* params) {
params->protocol = net::ClientProtocol::kHttp;
params->listen_port = 8080;
} else if (url.scheme() == "redir") {
#if defined(OS_LINUX)
#if BUILDFLAG(IS_LINUX)
params->protocol = net::ClientProtocol::kRedir;
params->listen_port = 1080;
#else
@ -463,18 +464,29 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
} // namespace net
int main(int argc, char* argv[]) {
naive_partition_alloc_support::ReconfigureEarly();
url::AddStandardScheme("quic",
url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION);
base::FeatureList::InitializeInstance(
"PartitionConnectionsByNetworkIsolationKey", std::string());
base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
base::ThreadPoolInstance::CreateAndStartWithDefaultParams("naive");
base::AtExitManager exit_manager;
net::ClientSocketPoolManager::set_max_sockets_per_pool(
net::HttpNetworkSession::NORMAL_SOCKET_POOL,
kDefaultMaxSocketsPerPool * kExpectedMaxUsers);
net::ClientSocketPoolManager::set_max_sockets_per_proxy_server(
net::HttpNetworkSession::NORMAL_SOCKET_POOL,
kDefaultMaxSocketsPerPool * kExpectedMaxUsers);
net::ClientSocketPoolManager::set_max_sockets_per_group(
net::HttpNetworkSession::NORMAL_SOCKET_POOL,
kDefaultMaxSocketsPerGroup * kExpectedMaxUsers);
#if defined(OS_MACOSX)
naive_partition_alloc_support::ReconfigureAfterFeatureListInit();
#if BUILDFLAG(IS_APPLE)
base::mac::ScopedNSAutoreleasePool pool;
#endif
base::AtExitManager exit_manager;
base::CommandLine::Init(argc, argv);
CommandLine cmdline;
@ -495,19 +507,13 @@ int main(int argc, char* argv[]) {
if (!ParseCommandLine(cmdline, &params)) {
return EXIT_FAILURE;
}
net::ClientSocketPoolManager::set_max_sockets_per_pool(
net::HttpNetworkSession::NORMAL_SOCKET_POOL,
kDefaultMaxSocketsPerPool * kExpectedMaxUsers);
net::ClientSocketPoolManager::set_max_sockets_per_proxy_server(
net::HttpNetworkSession::NORMAL_SOCKET_POOL,
kDefaultMaxSocketsPerPool * kExpectedMaxUsers);
net::ClientSocketPoolManager::set_max_sockets_per_group(
net::HttpNetworkSession::NORMAL_SOCKET_POOL,
kDefaultMaxSocketsPerGroup * kExpectedMaxUsers);
CHECK(logging::InitLogging(params.log_settings));
base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
base::ThreadPoolInstance::CreateAndStartWithDefaultParams("naive");
naive_partition_alloc_support::ReconfigureAfterTaskRunnerInit();
if (!params.ssl_key_path.empty()) {
net::SSLClientSocket::SetSSLKeyLogger(
std::make_unique<net::SSLKeyLoggerImpl>(params.ssl_key_path));
@ -537,8 +543,8 @@ int main(int argc, char* argv[]) {
scoped_refptr<net::CertNetFetcherURLRequest> cert_net_fetcher;
// The builtin verifier is supported but not enabled by default on Mac,
// falling back to CreateSystemVerifyProc() which drops the net fetcher.
// Skips defined(OS_MAC) for now, until it is enabled by default.
#if defined(OS_LINUX) || defined(OS_ANDROID)
// Skips BUILDFLAG(IS_MAC) for now, until it is enabled by default.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID)
cert_net_fetcher = base::MakeRefCounted<net::CertNetFetcherURLRequest>();
cert_net_fetcher->SetURLRequestContext(cert_context.get());
#endif

View File

@ -0,0 +1,170 @@
// Copyright 2021 The Chromium Authors
// Copyright 2022 klzgrad <kizdiv@gmail.com>.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/tools/naive/partition_alloc_support.h"
#include <string>
#include "base/allocator/allocator_check.h"
#include "base/allocator/buildflags.h"
#include "base/allocator/partition_alloc_features.h"
#include "base/allocator/partition_alloc_support.h"
#include "base/allocator/partition_allocator/partition_alloc_config.h"
#include "base/allocator/partition_allocator/shim/allocator_shim.h"
#include "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_partition_alloc.h"
#include "base/allocator/partition_allocator/thread_cache.h"
#include "base/feature_list.h"
#include "base/process/memory.h"
#include "base/time/time.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/system/sys_info.h"
#endif
#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
#include "base/allocator/partition_allocator/memory_reclaimer.h"
#include "base/threading/thread_task_runner_handle.h"
#endif
#if BUILDFLAG(IS_APPLE)
#include "base/allocator/early_zone_registration_mac.h"
#endif
namespace naive_partition_alloc_support {
void ReconfigureEarly() {
// chrome/app/chrome_exe_main_mac.cc: main()
#if BUILDFLAG(IS_APPLE)
partition_alloc::EarlyMallocZoneRegistration();
#endif
// content/app/content_main.cc: ChromeMain()
#if BUILDFLAG(IS_WIN)
#if BUILDFLAG(USE_ALLOCATOR_SHIM) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
// Call this early on in order to configure heap workarounds. This must be
// called from chrome.dll. This may be a NOP on some platforms.
allocator_shim::ConfigurePartitionAlloc();
#endif
#endif // BUILDFLAG(IS_WIN)
// content/app/content_main.cc: RunContentProcess()
#if BUILDFLAG(IS_APPLE) && BUILDFLAG(USE_ALLOCATOR_SHIM)
// The static initializer function for initializing PartitionAlloc
// InitializeDefaultMallocZoneWithPartitionAlloc() would be removed by the
// linker if allocator_shim.o is not referenced by the following call,
// resulting in undefined behavior of accessing uninitialized TLS
// data in PurgeCurrentThread() when PA is enabled.
allocator_shim::InitializeAllocatorShim();
#endif
// content/app/content_main.cc: RunContentProcess()
base::EnableTerminationOnOutOfMemory();
// content/app/content_main.cc: RunContentProcess()
base::EnableTerminationOnHeapCorruption();
// content/app/content_main.cc: RunContentProcess()
// content/app/content_main_runner_impl.cc: Initialize()
// ReconfigureEarlyish():
// These initializations are only relevant for PartitionAlloc-Everywhere
// builds.
#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
allocator_shim::EnablePartitionAllocMemoryReclaimer();
#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
// content/app/content_main.cc: RunContentProcess()
// content/app/content_main_runner_impl.cc: Initialize()
// If we are on a platform where the default allocator is overridden (e.g.
// with PartitionAlloc on most platforms) smoke-tests that the overriding
// logic is working correctly. If not causes a hard crash, as its unexpected
// absence has security implications.
CHECK(base::allocator::IsAllocatorInitialized());
}
void ReconfigureAfterFeatureListInit() {
// TODO(bartekn): Switch to DCHECK once confirmed there are no issues.
CHECK(base::FeatureList::GetInstance());
// Does not use any of the security features yet.
[[maybe_unused]] bool enable_brp = false;
[[maybe_unused]] bool enable_brp_zapping = false;
[[maybe_unused]] bool split_main_partition = false;
[[maybe_unused]] bool use_dedicated_aligned_partition = false;
[[maybe_unused]] bool process_affected_by_brp_flag = false;
#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
allocator_shim::ConfigurePartitions(
allocator_shim::EnableBrp(enable_brp),
allocator_shim::EnableBrpZapping(enable_brp_zapping),
allocator_shim::SplitMainPartition(split_main_partition),
allocator_shim::UseDedicatedAlignedPartition(
use_dedicated_aligned_partition),
allocator_shim::AlternateBucketDistribution(
base::features::kPartitionAllocAlternateBucketDistributionParam
.Get()));
#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
allocator_shim::internal::PartitionAllocMalloc::Allocator()
->EnableThreadCacheIfSupported();
if (base::FeatureList::IsEnabled(
base::features::kPartitionAllocLargeEmptySlotSpanRing)) {
allocator_shim::internal::PartitionAllocMalloc::Allocator()
->EnableLargeEmptySlotSpanRing();
allocator_shim::internal::PartitionAllocMalloc::AlignedAllocator()
->EnableLargeEmptySlotSpanRing();
}
#endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
}
void ReconfigureAfterTaskRunnerInit() {
#if defined(PA_THREAD_CACHE_SUPPORTED) && \
BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
base::allocator::StartThreadCachePeriodicPurge();
#if BUILDFLAG(IS_ANDROID)
// Lower thread cache limits to avoid stranding too much memory in the caches.
if (base::SysInfo::IsLowEndDevice()) {
::partition_alloc::ThreadCacheRegistry::Instance().SetThreadCacheMultiplier(
::partition_alloc::ThreadCache::kDefaultMultiplier / 2.);
}
#endif // BUILDFLAG(IS_ANDROID)
// Renderer processes are more performance-sensitive, increase thread cache
// limits.
if (/*is_performance_sensitive=*/true &&
base::FeatureList::IsEnabled(
base::features::kPartitionAllocLargeThreadCacheSize)) {
size_t largest_cached_size =
::partition_alloc::ThreadCacheLimits::kLargeSizeThreshold;
#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS)
// Devices almost always report less physical memory than what they actually
// have, so anything above 3GiB will catch 4GiB and above.
if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 3500)
largest_cached_size =
::partition_alloc::ThreadCacheLimits::kDefaultSizeThreshold;
#endif // BUILDFLAG(IS_ANDROID) && !defined(ARCH_CPU_64_BITS)
::partition_alloc::ThreadCache::SetLargestCachedSize(largest_cached_size);
}
#endif // defined(PA_THREAD_CACHE_SUPPORTED) &&
// BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
base::allocator::StartMemoryReclaimer(base::ThreadTaskRunnerHandle::Get());
#endif
if (base::FeatureList::IsEnabled(
base::features::kPartitionAllocSortActiveSlotSpans)) {
partition_alloc::PartitionRoot<
partition_alloc::internal::ThreadSafe>::EnableSortActiveSlotSpans();
}
}
} // namespace naive_partition_alloc_support

View File

@ -0,0 +1,17 @@
// Copyright 2021 The Chromium Authors
// Copyright 2022 klzgrad <kizdiv@gmail.com>.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_TOOLS_NAIVE_PARTITION_ALLOC_SUPPORT_H_
#define NET_TOOLS_NAIVE_PARTITION_ALLOC_SUPPORT_H_
namespace naive_partition_alloc_support {
void ReconfigureEarly();
void ReconfigureAfterFeatureListInit();
void ReconfigureAfterTaskRunnerInit();
} // namespace naive_partition_alloc_support
#endif // NET_TOOLS_NAIVE_PARTITION_ALLOC_SUPPORT_H_