diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-29 10:46:47 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-02 12:02:10 +0000 |
commit | 99677208ff3b216fdfec551fbe548da5520cd6fb (patch) | |
tree | 476a4865c10320249360e859d8fdd3e01833b03a /chromium/services | |
parent | c30a6232df03e1efbd9f3b226777b07e087a1122 (diff) | |
download | qtwebengine-chromium-99677208ff3b216fdfec551fbe548da5520cd6fb.tar.gz |
BASELINE: Update Chromium to 86.0.4240.124
Change-Id: Ide0ff151e94cd665ae6521a446995d34a9d1d644
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/services')
561 files changed, 12086 insertions, 13977 deletions
diff --git a/chromium/services/BUILD.gn b/chromium/services/BUILD.gn index cab26e2e9fe..847c1f82650 100644 --- a/chromium/services/BUILD.gn +++ b/chromium/services/BUILD.gn @@ -94,7 +94,7 @@ if (!is_ios) { } if (is_android) { - junit_binary("service_junit_tests") { + junit_binary("services_junit_tests") { sources = [ "device/generic_sensor/android/junit/src/org/chromium/device/sensors/PlatformSensorAndProviderTest.java", "device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java", @@ -127,7 +127,7 @@ if (is_android) { ] } - android_library("service_javatests") { + android_library("services_javatests") { testonly = true sources = [ "shape_detection/android/javatests/src/org/chromium/shape_detection/BarcodeDetectionImplTest.java", diff --git a/chromium/services/audio/BUILD.gn b/chromium/services/audio/BUILD.gn index dd4911729ab..84b197a67f2 100644 --- a/chromium/services/audio/BUILD.gn +++ b/chromium/services/audio/BUILD.gn @@ -71,24 +71,19 @@ source_set("audio") { "//media", "//media/webrtc", "//services/audio/public/mojom", - "//services/service_manager/sandbox", ] - if (is_linux) { + if (is_linux || is_chromeos) { sources += [ "audio_sandbox_hook_linux.cc", "audio_sandbox_hook_linux.h", ] - public_deps += [ "//sandbox/linux:sandbox_services" ] - } - - if (is_win) { - sources += [ - "audio_sandbox_win.cc", - "audio_sandbox_win.h", + public_deps += [ + "//sandbox/linux:sandbox_services", + "//sandbox/policy", ] - public_deps += [ "//sandbox/win:sandbox" ] } + configs += [ "//build/config/compiler:wexit_time_destructors", "//media:media_config", diff --git a/chromium/services/audio/DEPS b/chromium/services/audio/DEPS index 5c8ac88773d..4e45e6d00ae 100644 --- a/chromium/services/audio/DEPS +++ b/chromium/services/audio/DEPS @@ -5,5 +5,4 @@ include_rules = [ "+media/webrtc", "+sandbox", "+services/audio/public", - "+services/service_manager/sandbox", ] diff --git a/chromium/services/audio/OWNERS b/chromium/services/audio/OWNERS index e0dbf0d3868..dfd776de26d 100644 --- a/chromium/services/audio/OWNERS +++ b/chromium/services/audio/OWNERS @@ -3,6 +3,5 @@ dalecurtis@chromium.org miu@chromium.org per-file audio_sandbox_hook_linux.*=file://sandbox/linux/OWNERS -per-file audio_sandbox_win.*=file://sandbox/win/OWNERS # COMPONENT: Internals>Media>Audio diff --git a/chromium/services/audio/audio_sandbox_hook_linux.cc b/chromium/services/audio/audio_sandbox_hook_linux.cc index dad4537c4c6..e7928a96372 100644 --- a/chromium/services/audio/audio_sandbox_hook_linux.cc +++ b/chromium/services/audio/audio_sandbox_hook_linux.cc @@ -174,9 +174,9 @@ void LoadAudioLibraries() { } // namespace -bool AudioPreSandboxHook(service_manager::SandboxLinux::Options options) { +bool AudioPreSandboxHook(sandbox::policy::SandboxLinux::Options options) { LoadAudioLibraries(); - auto* instance = service_manager::SandboxLinux::GetInstance(); + auto* instance = sandbox::policy::SandboxLinux::GetInstance(); instance->StartBrokerProcess(MakeBrokerCommandSet({ sandbox::syscall_broker::COMMAND_ACCESS, #if defined(USE_PULSEAUDIO) @@ -188,7 +188,7 @@ bool AudioPreSandboxHook(service_manager::SandboxLinux::Options options) { sandbox::syscall_broker::COMMAND_UNLINK, }), GetAudioFilePermissions(), - service_manager::SandboxLinux::PreSandboxHook(), + sandbox::policy::SandboxLinux::PreSandboxHook(), options); // TODO(https://crbug.com/850878) enable namespace sandbox. Currently, if diff --git a/chromium/services/audio/audio_sandbox_hook_linux.h b/chromium/services/audio/audio_sandbox_hook_linux.h index 528ceaa0790..65a17ccf09e 100644 --- a/chromium/services/audio/audio_sandbox_hook_linux.h +++ b/chromium/services/audio/audio_sandbox_hook_linux.h @@ -5,13 +5,13 @@ #ifndef SERVICES_AUDIO_AUDIO_SANDBOX_HOOK_LINUX_H_ #define SERVICES_AUDIO_AUDIO_SANDBOX_HOOK_LINUX_H_ -#include "services/service_manager/sandbox/linux/sandbox_linux.h" +#include "sandbox/policy/linux/sandbox_linux.h" namespace audio { // Load audio shared libraries and setup allowed commands and filesystem // permissions for audio service sandboxed process. -bool AudioPreSandboxHook(service_manager::SandboxLinux::Options options); +bool AudioPreSandboxHook(sandbox::policy::SandboxLinux::Options options); } // namespace audio diff --git a/chromium/services/audio/audio_sandbox_win.cc b/chromium/services/audio/audio_sandbox_win.cc deleted file mode 100644 index c6f1c552383..00000000000 --- a/chromium/services/audio/audio_sandbox_win.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/audio/audio_sandbox_win.h" - -#include "sandbox/win/src/sandbox_policy.h" - -// NOTE: changes to this code need to be reviewed by the security team. - -namespace audio { - -//------------------------------------------------------------------------------ -// Public audio service sandbox configuration extension functions. -//------------------------------------------------------------------------------ -// -// Default policy: -// -// lockdown_level_(sandbox::USER_LOCKDOWN), -// initial_level_(sandbox::USER_RESTRICTED_SAME_ACCESS), -// -// job_level_(sandbox::JOB_LOCKDOWN), -// -// integrity_level_(sandbox::INTEGRITY_LEVEL_LOW), -// delayed_integrity_level_(sandbox::INTEGRITY_LEVEL_UNTRUSTED), - -bool AudioPreSpawnTarget(sandbox::TargetPolicy* policy) { - // Audio process privilege requirements: - // - Lockdown level of USER_NON_ADMIN - // - Delayed integrity level of INTEGRITY_LEVEL_LOW - // - // For audio streams to create shared memory regions, lockdown level must be - // at least USER_LIMITED and delayed integrity level INTEGRITY_LEVEL_LOW, - // otherwise CreateFileMapping() will fail with error code ERROR_ACCESS_DENIED - // (0x5). - // - // For audio input streams to use ISimpleAudioVolume interface, lockdown - // level must be set to USER_NON_ADMIN, otherwise - // WASAPIAudioInputStream::Open() will fail with error code E_ACCESSDENIED - // (0x80070005) when trying to get a reference to ISimpleAudioVolume - // interface. See - // https://cs.chromium.org/chromium/src/media/audio/win/audio_low_latency_input_win.cc - // Use USER_RESTRICTED_NON_ADMIN over USER_NON_ADMIN to prevent failures when - // AppLocker and similar application whitelisting solutions are in place. - policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, - sandbox::USER_RESTRICTED_NON_ADMIN); - policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); - - // Custom default policy allowing audio drivers to read device properties - // (https://crbug.com/883326). - policy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); - policy->SetLockdownDefaultDacl(); - policy->SetAlternateDesktop(true); - - return true; -} - -} // namespace audio diff --git a/chromium/services/audio/audio_sandbox_win.h b/chromium/services/audio/audio_sandbox_win.h deleted file mode 100644 index 8ce8cb0c7e1..00000000000 --- a/chromium/services/audio/audio_sandbox_win.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_AUDIO_AUDIO_SANDBOX_WIN_H_ -#define SERVICES_AUDIO_AUDIO_SANDBOX_WIN_H_ - -namespace sandbox { -class TargetPolicy; -} - -// These sandbox-config extension functions should be called from -// UtilitySandboxedProcessLauncherDelegate on Windows (or the appropriate -// Delegate if SandboxType::kAudio is removed from SandboxType::kUtility). -// -// NOTE: changes to this code need to be reviewed by the security team. - -namespace audio { - -// PreSpawnTarget extension. -bool AudioPreSpawnTarget(sandbox::TargetPolicy* policy); - -} // namespace audio - -#endif // SERVICES_AUDIO_AUDIO_SANDBOX_WIN_H_ diff --git a/chromium/services/audio/loopback_stream_unittest.cc b/chromium/services/audio/loopback_stream_unittest.cc index e1e7d53b084..aa7ee9b38a2 100644 --- a/chromium/services/audio/loopback_stream_unittest.cc +++ b/chromium/services/audio/loopback_stream_unittest.cc @@ -152,8 +152,7 @@ class LoopbackStreamTest : public testing::Test { sources_.emplace_back(std::make_unique<FakeLoopbackGroupMember>( media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY, media::GuessChannelLayout(channels), sample_rate, - (sample_rate * kBufferDuration) / - base::TimeDelta::FromSeconds(1)))); + (sample_rate * kBufferDuration).InSeconds()))); coordinator_.RegisterMember(group_id_, sources_.back().get()); return sources_.back().get(); } diff --git a/chromium/services/audio/service.cc b/chromium/services/audio/service.cc index 23bb03023ac..5eacb4c74c0 100644 --- a/chromium/services/audio/service.cc +++ b/chromium/services/audio/service.cc @@ -24,7 +24,7 @@ #include "services/audio/service_metrics.h" #include "services/audio/system_info.h" -#if defined(OS_MACOSX) +#if defined(OS_APPLE) #include "media/audio/mac/audio_device_listener_mac.h" #endif @@ -158,7 +158,7 @@ void Service::BindTestingApi( void Service::InitializeDeviceMonitor() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); -#if defined(OS_MACOSX) +#if defined(OS_APPLE) if (audio_device_listener_mac_) return; diff --git a/chromium/services/audio/service.h b/chromium/services/audio/service.h index cf433ad39ed..0b8343bba04 100644 --- a/chromium/services/audio/service.h +++ b/chromium/services/audio/service.h @@ -115,7 +115,7 @@ class Service : public mojom::AudioService { std::unique_ptr<AudioManagerAccessor> audio_manager_accessor_; const bool enable_remote_client_support_; std::unique_ptr<base::SystemMonitor> system_monitor_; -#if defined(OS_MACOSX) +#if defined(OS_MAC) std::unique_ptr<media::AudioDeviceListenerMac> audio_device_listener_mac_; #endif std::unique_ptr<SystemInfo> system_info_; diff --git a/chromium/services/audio/snooper_node_unittest.cc b/chromium/services/audio/snooper_node_unittest.cc index 63ac9c0aba9..31c606340f6 100644 --- a/chromium/services/audio/snooper_node_unittest.cc +++ b/chromium/services/audio/snooper_node_unittest.cc @@ -336,7 +336,8 @@ TEST_P(SnooperNodeTest, MAYBE_ContinuousAudioFlowAdaptsToSkew) { (output_skew * output_delay().InSecondsF())) * output_params().sample_rate(); const double frames_in_one_millisecond = - output_params().sample_rate() / 1000.0; + output_params().sample_rate() / + double{base::Time::kMillisecondsPerSecond}; EXPECT_NEAR(expected_end_of_silence_position, consumer()->FindEndOfSilence(0, 0), frames_in_one_millisecond); @@ -416,7 +417,7 @@ TEST_P(SnooperNodeTest, HandlesMissingInput) { const int output_frames_in_one_second = output_params().sample_rate(); const int output_frames_in_a_quarter_second = output_frames_in_one_second / 4; const int output_frames_in_20_milliseconds = - output_frames_in_one_second * 20 / 1000; + output_frames_in_one_second * 20 / base::Time::kMillisecondsPerSecond; int output_silence_position = ((kInputAdvanceTime + output_delay()).InSecondsF() + 1.0) * output_params().sample_rate(); @@ -624,7 +625,7 @@ double MapTimeOffsetToATone(base::TimeDelta offset) { constexpr double kMaxFrequency = 2000; constexpr int kNumToneSteps = 10; - const int64_t step_number = offset / (kTestDuration / kNumToneSteps); + const int64_t step_number = offset.IntDiv(kTestDuration / kNumToneSteps); const double t = static_cast<double>(step_number) / kNumToneSteps; return kMinFrequency + t * (kMaxFrequency - kMinFrequency); } diff --git a/chromium/services/audio/sync_reader.cc b/chromium/services/audio/sync_reader.cc index fbd4b1dc4ab..3b7b5537f2c 100644 --- a/chromium/services/audio/sync_reader.cc +++ b/chromium/services/audio/sync_reader.cc @@ -54,7 +54,7 @@ SyncReader::SyncReader( renderer_callback_count_(0), renderer_missed_callback_count_(0), trailing_renderer_missed_callback_count_(0), -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(OS_MAC) || defined(OS_CHROMEOS) maximum_wait_time_(params.GetBufferDuration() / 2), #else // TODO(dalecurtis): Investigate if we can reduce this on all platforms. diff --git a/chromium/services/cert_verifier/cert_net_url_loader/BUILD.gn b/chromium/services/cert_verifier/cert_net_url_loader/BUILD.gn index 009e8c23e52..3efdc1e8994 100644 --- a/chromium/services/cert_verifier/cert_net_url_loader/BUILD.gn +++ b/chromium/services/cert_verifier/cert_net_url_loader/BUILD.gn @@ -3,10 +3,9 @@ # found in the LICENSE file. import("//build/config/crypto.gni") -import("//build/config/jumbo.gni") import("//testing/test.gni") -jumbo_component("cert_net_url_loader") { +component("cert_net_url_loader") { sources = [ "cert_net_fetcher_url_loader.cc", "cert_net_fetcher_url_loader.h", diff --git a/chromium/services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader_unittest.cc b/chromium/services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader_unittest.cc index 86981803d23..264950e90d6 100644 --- a/chromium/services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader_unittest.cc +++ b/chromium/services/cert_verifier/cert_net_url_loader/cert_net_fetcher_url_loader_unittest.cc @@ -24,6 +24,7 @@ #include "net/test/gtest_util.h" #include "net/test/test_with_task_environment.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "net/url_request/url_request.h" #include "net/url_request/url_request_filter.h" #include "net/url_request/url_request_interceptor.h" #include "services/cert_verifier/cert_net_url_loader/cert_net_fetcher_test.h" diff --git a/chromium/services/data_decoder/BUILD.gn b/chromium/services/data_decoder/BUILD.gn index fa455db7aa3..adb398ed3ca 100644 --- a/chromium/services/data_decoder/BUILD.gn +++ b/chromium/services/data_decoder/BUILD.gn @@ -10,10 +10,6 @@ source_set("lib") { "data_decoder_service.h", "json_parser_impl.cc", "json_parser_impl.h", - "web_bundle_parser.cc", - "web_bundle_parser.h", - "web_bundle_parser_factory.cc", - "web_bundle_parser_factory.h", "web_bundler.cc", "web_bundler.h", "xml_parser.cc", @@ -31,7 +27,7 @@ source_set("lib") { deps = [ "//base", - "//components/cbor", + "//components/web_package", "//mojo/public/cpp/bindings", "//net", "//skia", @@ -60,8 +56,6 @@ source_set("tests") { "public/cpp/json_sanitizer_unittest.cc", "public/cpp/safe_web_bundle_parser_unittest.cc", "public/cpp/safe_xml_parser_unittest.cc", - "web_bundle_parser_factory_unittest.cc", - "web_bundle_parser_unittest.cc", "xml_parser_unittest.cc", ] @@ -73,7 +67,6 @@ source_set("tests") { ":lib", "//base", "//base/test:test_support", - "//components/cbor", "//services/data_decoder/public/cpp", "//services/data_decoder/public/cpp:test_support", "//skia", @@ -81,6 +74,8 @@ source_set("tests") { "//ui/gfx", ] + data = [ "//components/test/data/web_package/" ] + if (!is_ios) { sources += [ "image_decoder_impl_unittest.cc" ] deps += [ @@ -96,16 +91,6 @@ source_set("tests") { } } -fuzzer_test("web_bundle_parser_fuzzer") { - sources = [ "web_bundle_parser_fuzzer.cc" ] - deps = [ - ":lib", - "//base", - "//mojo/core/embedder", - ] - seed_corpus = "//services/test/data/web_bundle" -} - fuzzer_test("xml_parser_fuzzer") { sources = [ "xml_parser_fuzzer.cc" ] deps = [ diff --git a/chromium/services/data_decoder/DEPS b/chromium/services/data_decoder/DEPS index f8f08e37243..bd2835402c1 100644 --- a/chromium/services/data_decoder/DEPS +++ b/chromium/services/data_decoder/DEPS @@ -1,5 +1,5 @@ include_rules = [ - "+components/cbor", + "+components/web_package", "+device/bluetooth/public", "+gin", "+net", diff --git a/chromium/services/data_decoder/data_decoder_service.cc b/chromium/services/data_decoder/data_decoder_service.cc index f841ce45bfa..96f6a3aeb85 100644 --- a/chromium/services/data_decoder/data_decoder_service.cc +++ b/chromium/services/data_decoder/data_decoder_service.cc @@ -11,11 +11,11 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" +#include "components/web_package/web_bundle_parser_factory.h" #include "mojo/public/cpp/bindings/generic_pending_receiver.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "services/data_decoder/json_parser_impl.h" #include "services/data_decoder/public/mojom/image_decoder.mojom.h" -#include "services/data_decoder/web_bundle_parser_factory.h" #include "services/data_decoder/web_bundler.h" #include "services/data_decoder/xml_parser.h" @@ -70,12 +70,14 @@ void DataDecoderService::BindXmlParser( } void DataDecoderService::BindWebBundleParserFactory( - mojo::PendingReceiver<mojom::WebBundleParserFactory> receiver) { + mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory> + receiver) { if (web_bundle_parser_factory_binder_) { web_bundle_parser_factory_binder_.Run(std::move(receiver)); } else { - mojo::MakeSelfOwnedReceiver(std::make_unique<WebBundleParserFactory>(), - std::move(receiver)); + mojo::MakeSelfOwnedReceiver( + std::make_unique<web_package::WebBundleParserFactory>(), + std::move(receiver)); } } diff --git a/chromium/services/data_decoder/data_decoder_service.h b/chromium/services/data_decoder/data_decoder_service.h index b3999002ffe..3e079b2a9f4 100644 --- a/chromium/services/data_decoder/data_decoder_service.h +++ b/chromium/services/data_decoder/data_decoder_service.h @@ -8,12 +8,12 @@ #include <memory> #include "base/macros.h" +#include "components/web_package/mojom/web_bundle_parser.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "services/data_decoder/public/mojom/data_decoder_service.mojom.h" #include "services/data_decoder/public/mojom/image_decoder.mojom.h" #include "services/data_decoder/public/mojom/json_parser.mojom.h" -#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h" #include "services/data_decoder/public/mojom/web_bundler.mojom.h" #include "services/data_decoder/public/mojom/xml_parser.mojom.h" @@ -50,8 +50,9 @@ class DataDecoderService : public mojom::DataDecoderService { // WebBundleParserFactory in subsequent // BindWebBundleParserFactory() calls. void SetWebBundleParserFactoryBinderForTesting( - base::RepeatingCallback< - void(mojo::PendingReceiver<mojom::WebBundleParserFactory>)> binder) { + base::RepeatingCallback<void( + mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>)> + binder) { web_bundle_parser_factory_binder_ = binder; } @@ -71,7 +72,8 @@ class DataDecoderService : public mojom::DataDecoderService { mojo::PendingReceiver<mojom::JsonParser> receiver) override; void BindXmlParser(mojo::PendingReceiver<mojom::XmlParser> receiver) override; void BindWebBundleParserFactory( - mojo::PendingReceiver<mojom::WebBundleParserFactory> receiver) override; + mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory> + receiver) override; void BindWebBundler( mojo::PendingReceiver<mojom::WebBundler> receiver) override; @@ -87,7 +89,7 @@ class DataDecoderService : public mojom::DataDecoderService { bool drop_image_decoders_ = false; bool drop_json_parsers_ = false; base::RepeatingCallback<void( - mojo::PendingReceiver<mojom::WebBundleParserFactory>)> + mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory>)> web_bundle_parser_factory_binder_; base::RepeatingCallback<void(mojo::PendingReceiver<mojom::WebBundler>)> web_bundler_binder_; diff --git a/chromium/services/data_decoder/image_decoder_impl.cc b/chromium/services/data_decoder/image_decoder_impl.cc index 9ba119aa9fe..16677c88c0e 100644 --- a/chromium/services/data_decoder/image_decoder_impl.cc +++ b/chromium/services/data_decoder/image_decoder_impl.cc @@ -8,7 +8,6 @@ #include <utility> -#include "mojo/public/cpp/bindings/strong_binding.h" #include "skia/ext/image_operations.h" #include "third_party/blink/public/platform/web_data.h" #include "third_party/blink/public/platform/web_size.h" diff --git a/chromium/services/data_decoder/public/cpp/BUILD.gn b/chromium/services/data_decoder/public/cpp/BUILD.gn index 4dd89896df9..16ef9b7c14a 100644 --- a/chromium/services/data_decoder/public/cpp/BUILD.gn +++ b/chromium/services/data_decoder/public/cpp/BUILD.gn @@ -77,12 +77,8 @@ source_set("test_support") { sources = [ "test_support/in_process_data_decoder.cc", "test_support/in_process_data_decoder.h", - "test_support/web_bundle_builder.cc", - "test_support/web_bundle_builder.h", ] - deps = [ "//components/cbor" ] - public_deps = [ ":cpp", "//base", diff --git a/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser.cc b/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser.cc index bd8c57d2809..ac1d849e668 100644 --- a/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser.cc +++ b/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser.cc @@ -34,7 +34,7 @@ base::File::Error SafeWebBundleParser::OpenFile(base::File file) { } void SafeWebBundleParser::OpenDataSource( - mojo::PendingRemote<mojom::BundleDataSource> data_source) { + mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source) { DCHECK(disconnected_); GetFactory()->GetParserForDataSource(parser_.BindNewPipeAndPassReceiver(), std::move(data_source)); @@ -45,14 +45,15 @@ void SafeWebBundleParser::OpenDataSource( } void SafeWebBundleParser::ParseMetadata( - mojom::WebBundleParser::ParseMetadataCallback callback) { + web_package::mojom::WebBundleParser::ParseMetadataCallback callback) { // This method is designed to be called once. So, allowing only once // simultaneous request is fine enough. if (disconnected_ || !metadata_callback_.is_null()) { std::move(callback).Run( - nullptr, mojom::BundleMetadataParseError::New( - mojom::BundleParseErrorType::kParserInternalError, - GURL() /* fallback_url */, kConnectionError)); + nullptr, + web_package::mojom::BundleMetadataParseError::New( + web_package::mojom::BundleParseErrorType::kParserInternalError, + GURL() /* fallback_url */, kConnectionError)); return; } metadata_callback_ = std::move(callback); @@ -63,16 +64,17 @@ void SafeWebBundleParser::ParseMetadata( void SafeWebBundleParser::ParseResponse( uint64_t response_offset, uint64_t response_length, - mojom::WebBundleParser::ParseResponseCallback callback) { + web_package::mojom::WebBundleParser::ParseResponseCallback callback) { // This method allows simultaneous multiple requests. But if the unique ID // overflows, and the previous request that owns the same ID hasn't finished, // we just make the new request fail with kConnectionError. if (disconnected_ || response_callbacks_.contains(response_callback_next_id_)) { std::move(callback).Run( - nullptr, mojom::BundleResponseParseError::New( - mojom::BundleParseErrorType::kParserInternalError, - kConnectionError)); + nullptr, + web_package::mojom::BundleResponseParseError::New( + web_package::mojom::BundleParseErrorType::kParserInternalError, + kConnectionError)); return; } size_t callback_id = response_callback_next_id_++; @@ -82,7 +84,7 @@ void SafeWebBundleParser::ParseResponse( base::Unretained(this), callback_id)); } -mojom::WebBundleParserFactory* SafeWebBundleParser::GetFactory() { +web_package::mojom::WebBundleParserFactory* SafeWebBundleParser::GetFactory() { if (!factory_) { data_decoder_.GetService()->BindWebBundleParserFactory( factory_.BindNewPipeAndPassReceiver()); @@ -100,30 +102,32 @@ void SafeWebBundleParser::OnDisconnect() { disconnected_ = true; if (!metadata_callback_.is_null()) std::move(metadata_callback_) - .Run(nullptr, mojom::BundleMetadataParseError::New( - mojom::BundleParseErrorType::kParserInternalError, - GURL() /* fallback_url */, kConnectionError)); + .Run(nullptr, + web_package::mojom::BundleMetadataParseError::New( + web_package::mojom::BundleParseErrorType::kParserInternalError, + GURL() /* fallback_url */, kConnectionError)); for (auto& callback : response_callbacks_) std::move(callback.second) - .Run(nullptr, mojom::BundleResponseParseError::New( - mojom::BundleParseErrorType::kParserInternalError, - kConnectionError)); + .Run(nullptr, + web_package::mojom::BundleResponseParseError::New( + web_package::mojom::BundleParseErrorType::kParserInternalError, + kConnectionError)); response_callbacks_.clear(); if (disconnect_callback_) std::move(disconnect_callback_).Run(); } void SafeWebBundleParser::OnMetadataParsed( - mojom::BundleMetadataPtr metadata, - mojom::BundleMetadataParseErrorPtr error) { + web_package::mojom::BundleMetadataPtr metadata, + web_package::mojom::BundleMetadataParseErrorPtr error) { DCHECK(!metadata_callback_.is_null()); std::move(metadata_callback_).Run(std::move(metadata), std::move(error)); } void SafeWebBundleParser::OnResponseParsed( size_t callback_id, - mojom::BundleResponsePtr response, - mojom::BundleResponseParseErrorPtr error) { + web_package::mojom::BundleResponsePtr response, + web_package::mojom::BundleResponseParseErrorPtr error) { auto it = response_callbacks_.find(callback_id); DCHECK(it != response_callbacks_.end()); auto callback = std::move(it->second); diff --git a/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser.h b/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser.h index 2ef665c1bef..d2097aa1d2c 100644 --- a/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser.h +++ b/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser.h @@ -10,16 +10,16 @@ #include "base/containers/flat_map.h" #include "base/files/file.h" #include "base/optional.h" +#include "components/web_package/mojom/web_bundle_parser.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/data_decoder/public/cpp/data_decoder.h" -#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h" namespace data_decoder { -// A class to wrap remote mojom::WebBundleParserFactory and -// mojom::WebBundleParser service. +// A class to wrap remote web_package::mojom::WebBundleParserFactory and +// web_package::mojom::WebBundleParser service. class SafeWebBundleParser { public: SafeWebBundleParser(); @@ -31,37 +31,41 @@ class SafeWebBundleParser { // Binds |this| instance to the given |data_source| for subsequent parse // calls. - void OpenDataSource(mojo::PendingRemote<mojom::BundleDataSource> data_source); + void OpenDataSource( + mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source); - // Parses metadata. See mojom::WebBundleParser::ParseMetadata for + // Parses metadata. See web_package::mojom::WebBundleParser::ParseMetadata for // details. This method fails when it's called before the previous call // finishes. - void ParseMetadata(mojom::WebBundleParser::ParseMetadataCallback callback); + void ParseMetadata( + web_package::mojom::WebBundleParser::ParseMetadataCallback callback); - // Parses response. See mojom::WebBundleParser::ParseResponse for + // Parses response. See web_package::mojom::WebBundleParser::ParseResponse for // details. - void ParseResponse(uint64_t response_offset, - uint64_t response_length, - mojom::WebBundleParser::ParseResponseCallback callback); + void ParseResponse( + uint64_t response_offset, + uint64_t response_length, + web_package::mojom::WebBundleParser::ParseResponseCallback callback); // Sets a callback to be called when the data_decoder service connection is // terminated. void SetDisconnectCallback(base::OnceClosure callback); private: - mojom::WebBundleParserFactory* GetFactory(); + web_package::mojom::WebBundleParserFactory* GetFactory(); void OnDisconnect(); - void OnMetadataParsed(mojom::BundleMetadataPtr metadata, - mojom::BundleMetadataParseErrorPtr error); + void OnMetadataParsed(web_package::mojom::BundleMetadataPtr metadata, + web_package::mojom::BundleMetadataParseErrorPtr error); void OnResponseParsed(size_t callback_id, - mojom::BundleResponsePtr response, - mojom::BundleResponseParseErrorPtr error); + web_package::mojom::BundleResponsePtr response, + web_package::mojom::BundleResponseParseErrorPtr error); DataDecoder data_decoder_; - mojo::Remote<mojom::WebBundleParserFactory> factory_; - mojo::Remote<mojom::WebBundleParser> parser_; - mojom::WebBundleParser::ParseMetadataCallback metadata_callback_; - base::flat_map<size_t, mojom::WebBundleParser::ParseResponseCallback> + mojo::Remote<web_package::mojom::WebBundleParserFactory> factory_; + mojo::Remote<web_package::mojom::WebBundleParser> parser_; + web_package::mojom::WebBundleParser::ParseMetadataCallback metadata_callback_; + base::flat_map<size_t, + web_package::mojom::WebBundleParser::ParseResponseCallback> response_callbacks_; base::OnceClosure disconnect_callback_; size_t response_callback_next_id_ = 0; diff --git a/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc b/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc index 8709166d18b..c6877ede504 100644 --- a/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc +++ b/chromium/services/data_decoder/public/cpp/safe_web_bundle_parser_unittest.cc @@ -29,17 +29,18 @@ base::File OpenTestFile(const base::FilePath& path) { base::FilePath test_data_dir; base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir); test_data_dir = test_data_dir.Append( - base::FilePath(FILE_PATH_LITERAL("services/test/data/web_bundle"))); + base::FilePath(FILE_PATH_LITERAL("components/test/data/web_package"))); test_data_dir = test_data_dir.Append(path); return base::File(test_data_dir, base::File::FLAG_OPEN | base::File::FLAG_READ); } -class MockFactory final : public mojom::WebBundleParserFactory { +class MockFactory final : public web_package::mojom::WebBundleParserFactory { public: - class MockParser final : public mojom::WebBundleParser { + class MockParser final : public web_package::mojom::WebBundleParser { public: - MockParser(mojo::PendingReceiver<mojom::WebBundleParser> receiver) + explicit MockParser( + mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver) : receiver_(this, std::move(receiver)) {} bool IsParseMetadataCalled() { return !metadata_callback_.is_null(); } @@ -48,7 +49,7 @@ class MockFactory final : public mojom::WebBundleParserFactory { void Disconnect() { receiver_.reset(); } private: - // mojom::WebBundleParser implementation. + // web_package::mojom::WebBundleParser implementation. void ParseMetadata(ParseMetadataCallback callback) override { metadata_callback_ = std::move(callback); } @@ -60,14 +61,15 @@ class MockFactory final : public mojom::WebBundleParserFactory { ParseMetadataCallback metadata_callback_; ParseResponseCallback response_callback_; - mojo::Receiver<mojom::WebBundleParser> receiver_; + mojo::Receiver<web_package::mojom::WebBundleParser> receiver_; DISALLOW_COPY_AND_ASSIGN(MockParser); }; MockFactory() {} void AddReceiver( - mojo::PendingReceiver<mojom::WebBundleParserFactory> receiver) { + mojo::PendingReceiver<web_package::mojom::WebBundleParserFactory> + receiver) { receivers_.Add(this, std::move(receiver)); } MockParser* GetCreatedParser() { @@ -77,33 +79,36 @@ class MockFactory final : public mojom::WebBundleParserFactory { void DeleteParser() { parser_.reset(); } private: - // mojom::WebBundleParserFactory implementation. - void GetParserForFile(mojo::PendingReceiver<mojom::WebBundleParser> receiver, - base::File file) override { + // web_package::mojom::WebBundleParserFactory implementation. + void GetParserForFile( + mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver, + base::File file) override { parser_ = std::make_unique<MockParser>(std::move(receiver)); } void GetParserForDataSource( - mojo::PendingReceiver<mojom::WebBundleParser> receiver, - mojo::PendingRemote<mojom::BundleDataSource> data_source) override { + mojo::PendingReceiver<web_package::mojom::WebBundleParser> receiver, + mojo::PendingRemote<web_package::mojom::BundleDataSource> data_source) + override { parser_ = std::make_unique<MockParser>(std::move(receiver)); } std::unique_ptr<MockParser> parser_; - mojo::ReceiverSet<data_decoder::mojom::WebBundleParserFactory> receivers_; + mojo::ReceiverSet<web_package::mojom::WebBundleParserFactory> receivers_; DISALLOW_COPY_AND_ASSIGN(MockFactory); }; -class MockDataSource final : public mojom::BundleDataSource { +class MockDataSource final : public web_package::mojom::BundleDataSource { public: - MockDataSource(mojo::PendingReceiver<mojom::BundleDataSource> receiver) + explicit MockDataSource( + mojo::PendingReceiver<web_package::mojom::BundleDataSource> receiver) : receiver_(this, std::move(receiver)) {} private: - // Implements mojom::BundledDataSource. + // Implements web_package::mojom::BundledDataSource. void Read(uint64_t offset, uint64_t length, ReadCallback callback) override {} - mojo::Receiver<mojom::BundleDataSource> receiver_; + mojo::Receiver<web_package::mojom::BundleDataSource> receiver_; DISALLOW_COPY_AND_ASSIGN(MockDataSource); }; @@ -135,14 +140,14 @@ TEST_F(SafeWebBundleParserTest, ParseGoldenFile) { OpenTestFile(base::FilePath(FILE_PATH_LITERAL("hello.wbn"))); ASSERT_EQ(base::File::FILE_OK, parser.OpenFile(std::move(test_file))); - mojom::BundleMetadataPtr metadata_result; + web_package::mojom::BundleMetadataPtr metadata_result; { base::RunLoop run_loop; parser.ParseMetadata(base::BindOnce( [](base::OnceClosure quit_closure, - mojom::BundleMetadataPtr* metadata_result, - mojom::BundleMetadataPtr metadata, - mojom::BundleMetadataParseErrorPtr error) { + web_package::mojom::BundleMetadataPtr* metadata_result, + web_package::mojom::BundleMetadataPtr metadata, + web_package::mojom::BundleMetadataParseErrorPtr error) { EXPECT_TRUE(metadata); EXPECT_FALSE(error); if (metadata) @@ -156,7 +161,7 @@ TEST_F(SafeWebBundleParserTest, ParseGoldenFile) { const auto& requests = metadata_result->requests; ASSERT_EQ(requests.size(), 4u); - std::map<std::string, mojom::BundleResponsePtr> responses; + std::map<std::string, web_package::mojom::BundleResponsePtr> responses; for (auto& entry : requests) { base::RunLoop run_loop; parser.ParseResponse( @@ -164,9 +169,10 @@ TEST_F(SafeWebBundleParserTest, ParseGoldenFile) { entry.second->response_locations[0]->length, base::BindOnce( [](base::OnceClosure quit_closure, const std::string url, - std::map<std::string, mojom::BundleResponsePtr>* responses, - mojom::BundleResponsePtr response, - mojom::BundleResponseParseErrorPtr error) { + std::map<std::string, web_package::mojom::BundleResponsePtr>* + responses, + web_package::mojom::BundleResponsePtr response, + web_package::mojom::BundleResponseParseErrorPtr error) { EXPECT_TRUE(response); EXPECT_FALSE(error); if (response) @@ -196,8 +202,8 @@ TEST_F(SafeWebBundleParserTest, CallWithoutOpen) { SafeWebBundleParser parser; bool metadata_parsed = false; parser.ParseMetadata(base::BindOnce( - [](bool* metadata_parsed, mojom::BundleMetadataPtr metadata, - mojom::BundleMetadataParseErrorPtr error) { + [](bool* metadata_parsed, web_package::mojom::BundleMetadataPtr metadata, + web_package::mojom::BundleMetadataParseErrorPtr error) { EXPECT_FALSE(metadata); EXPECT_TRUE(error); if (error) @@ -211,8 +217,9 @@ TEST_F(SafeWebBundleParserTest, CallWithoutOpen) { parser.ParseResponse( 0u, 0u, base::BindOnce( - [](bool* response_parsed, mojom::BundleResponsePtr response, - mojom::BundleResponseParseErrorPtr error) { + [](bool* response_parsed, + web_package::mojom::BundleResponsePtr response, + web_package::mojom::BundleResponseParseErrorPtr error) { EXPECT_FALSE(response); EXPECT_TRUE(error); if (error) @@ -250,7 +257,7 @@ TEST_F(SafeWebBundleParserTest, ConnectionError) { SafeWebBundleParser parser; MockFactory* raw_factory = InitializeMockFactory(); - mojo::PendingRemote<mojom::BundleDataSource> remote_data_source; + mojo::PendingRemote<web_package::mojom::BundleDataSource> remote_data_source; auto data_source = std::make_unique<MockDataSource>( remote_data_source.InitWithNewPipeAndPassReceiver()); parser.OpenDataSource(std::move(remote_data_source)); @@ -260,8 +267,8 @@ TEST_F(SafeWebBundleParserTest, ConnectionError) { bool parsed = false; parser.ParseMetadata(base::BindOnce( [](base::OnceClosure quit_closure, bool* parsed, - mojom::BundleMetadataPtr metadata, - mojom::BundleMetadataParseErrorPtr error) { + web_package::mojom::BundleMetadataPtr metadata, + web_package::mojom::BundleMetadataParseErrorPtr error) { EXPECT_FALSE(metadata); EXPECT_TRUE(error); if (error) diff --git a/chromium/services/data_decoder/public/cpp/test_support/web_bundle_builder.cc b/chromium/services/data_decoder/public/cpp/test_support/web_bundle_builder.cc deleted file mode 100644 index 8155e97d512..00000000000 --- a/chromium/services/data_decoder/public/cpp/test_support/web_bundle_builder.cc +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/data_decoder/public/cpp/test_support/web_bundle_builder.h" - -namespace data_decoder { -namespace test { - -namespace { - -cbor::Value CreateByteString(base::StringPiece s) { - return cbor::Value(base::as_bytes(base::make_span(s))); -} - -cbor::Value CreateHeaderMap(const WebBundleBuilder::Headers& headers) { - cbor::Value::MapValue map; - for (const auto& pair : headers) - map.insert({CreateByteString(pair.first), CreateByteString(pair.second)}); - return cbor::Value(std::move(map)); -} - -} // namespace - -WebBundleBuilder::WebBundleBuilder(const std::string& fallback_url, - const std::string& manifest_url) - : fallback_url_(fallback_url) { - writer_config_.allow_invalid_utf8_for_testing = true; - if (!manifest_url.empty()) { - AddSection("manifest", - cbor::Value::InvalidUTF8StringValueForTesting(manifest_url)); - } -} - -WebBundleBuilder::~WebBundleBuilder() = default; - -void WebBundleBuilder::AddExchange(base::StringPiece url, - const Headers& response_headers, - base::StringPiece payload) { - AddIndexEntry(url, "", {AddResponse(response_headers, payload)}); -} - -WebBundleBuilder::ResponseLocation WebBundleBuilder::AddResponse( - const Headers& headers, - base::StringPiece payload) { - // We assume that the size of the CBOR header of the responses array is 1, - // which is true only if the responses array has no more than 23 elements. - DCHECK_LT(responses_.size(), 23u) - << "WebBundleBuilder cannot create bundles with more than 23 responses"; - - cbor::Value::ArrayValue response_array; - response_array.emplace_back(Encode(CreateHeaderMap(headers))); - response_array.emplace_back(CreateByteString(payload)); - cbor::Value response(response_array); - int64_t response_length = EncodedLength(response); - ResponseLocation result = {current_responses_offset_, response_length}; - current_responses_offset_ += response_length; - responses_.emplace_back(std::move(response)); - return result; -} - -void WebBundleBuilder::AddIndexEntry( - base::StringPiece url, - base::StringPiece variants_value, - std::vector<ResponseLocation> response_locations) { - cbor::Value::ArrayValue index_value_array; - index_value_array.emplace_back(CreateByteString(variants_value)); - for (const auto& location : response_locations) { - index_value_array.emplace_back(location.offset); - index_value_array.emplace_back(location.length); - } - index_.insert({cbor::Value::InvalidUTF8StringValueForTesting(url), - cbor::Value(index_value_array)}); -} - -void WebBundleBuilder::AddSection(base::StringPiece name, cbor::Value section) { - section_lengths_.emplace_back(name); - section_lengths_.emplace_back(EncodedLength(section)); - sections_.emplace_back(std::move(section)); -} - -void WebBundleBuilder::AddAuthority(cbor::Value::MapValue authority) { - authorities_.emplace_back(std::move(authority)); -} - -void WebBundleBuilder::AddVouchedSubset(cbor::Value::MapValue vouched_subset) { - vouched_subsets_.emplace_back(std::move(vouched_subset)); -} - -std::vector<uint8_t> WebBundleBuilder::CreateBundle() { - AddSection("index", cbor::Value(index_)); - if (!authorities_.empty() || !vouched_subsets_.empty()) { - cbor::Value::ArrayValue signatures_section; - signatures_section.emplace_back(std::move(authorities_)); - signatures_section.emplace_back(std::move(vouched_subsets_)); - AddSection("signatures", cbor::Value(std::move(signatures_section))); - } - AddSection("responses", cbor::Value(responses_)); - return Encode(CreateTopLevel()); -} - -cbor::Value WebBundleBuilder::CreateEncodedSigned( - base::StringPiece validity_url, - base::StringPiece auth_sha256, - int64_t date, - int64_t expires, - base::StringPiece url, - base::StringPiece header_sha256, - base::StringPiece payload_integrity_header) { - cbor::Value::ArrayValue subset_hash_value; - subset_hash_value.emplace_back(CreateByteString("")); // variants-value - subset_hash_value.emplace_back(CreateByteString(header_sha256)); - subset_hash_value.emplace_back(payload_integrity_header); - - cbor::Value::MapValue subset_hashes; - subset_hashes.emplace(url, std::move(subset_hash_value)); - - cbor::Value::MapValue signed_subset; - signed_subset.emplace("validity-url", validity_url); - signed_subset.emplace("auth-sha256", CreateByteString(auth_sha256)); - signed_subset.emplace("date", date); - signed_subset.emplace("expires", expires); - signed_subset.emplace("subset-hashes", std::move(subset_hashes)); - return cbor::Value(Encode(cbor::Value(signed_subset))); -} - -cbor::Value WebBundleBuilder::CreateTopLevel() { - cbor::Value::ArrayValue toplevel_array; - toplevel_array.emplace_back( - CreateByteString(u8"\U0001F310\U0001F4E6")); // "🌐📦" - toplevel_array.emplace_back(CreateByteString(base::StringPiece("b1\0\0", 4))); - toplevel_array.emplace_back( - cbor::Value::InvalidUTF8StringValueForTesting(fallback_url_)); - toplevel_array.emplace_back(Encode(cbor::Value(section_lengths_))); - toplevel_array.emplace_back(sections_); - toplevel_array.emplace_back(CreateByteString("")); // length (ignored) - return cbor::Value(toplevel_array); -} - -std::vector<uint8_t> WebBundleBuilder::Encode(const cbor::Value& value) { - return *cbor::Writer::Write(value, writer_config_); -} - -int64_t WebBundleBuilder::EncodedLength(const cbor::Value& value) { - return Encode(value).size(); -} - -} // namespace test -} // namespace data_decoder diff --git a/chromium/services/data_decoder/public/cpp/test_support/web_bundle_builder.h b/chromium/services/data_decoder/public/cpp/test_support/web_bundle_builder.h deleted file mode 100644 index 71267ac3ec7..00000000000 --- a/chromium/services/data_decoder/public/cpp/test_support/web_bundle_builder.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_DATA_DECODER_PUBLIC_CPP_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_ -#define SERVICES_DATA_DECODER_PUBLIC_CPP_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_ - -#include <string> -#include <utility> -#include <vector> - -#include "base/strings/string_piece.h" -#include "components/cbor/writer.h" - -namespace data_decoder { -namespace test { - -// This class can be used to create a Web Bundle binary in tests. -class WebBundleBuilder { - public: - using Headers = std::vector<std::pair<std::string, std::string>>; - struct ResponseLocation { - // /components/cbor uses int64_t for integer types. - int64_t offset; - int64_t length; - }; - - WebBundleBuilder(const std::string& fallback_url, - const std::string& manifest_url); - ~WebBundleBuilder(); - - void AddExchange(base::StringPiece url, - const Headers& response_headers, - base::StringPiece payload); - - ResponseLocation AddResponse(const Headers& headers, - base::StringPiece payload); - - void AddIndexEntry(base::StringPiece url, - base::StringPiece variants_value, - std::vector<ResponseLocation> response_locations); - void AddSection(base::StringPiece name, cbor::Value section); - void AddAuthority(cbor::Value::MapValue authority); - void AddVouchedSubset(cbor::Value::MapValue vouched_subset); - - std::vector<uint8_t> CreateBundle(); - - // Creates a signed-subset structure with single subset-hashes entry, - // and returns it as a CBOR bytestring. - cbor::Value CreateEncodedSigned(base::StringPiece validity_url, - base::StringPiece auth_sha256, - int64_t date, - int64_t expires, - base::StringPiece url, - base::StringPiece header_sha256, - base::StringPiece payload_integrity_header); - - private: - cbor::Value CreateTopLevel(); - std::vector<uint8_t> Encode(const cbor::Value& value); - - int64_t EncodedLength(const cbor::Value& value); - - cbor::Writer::Config writer_config_; - std::string fallback_url_; - cbor::Value::ArrayValue section_lengths_; - cbor::Value::ArrayValue sections_; - cbor::Value::MapValue index_; - cbor::Value::ArrayValue responses_; - cbor::Value::ArrayValue authorities_; - cbor::Value::ArrayValue vouched_subsets_; - - // 1 for the CBOR header byte. See the comment at the top of AddResponse(). - int64_t current_responses_offset_ = 1; -}; - -} // namespace test -} // namespace data_decoder - -#endif // SERVICES_DATA_DECODER_PUBLIC_CPP_TEST_SUPPORT_WEB_BUNDLE_BUILDER_H_ diff --git a/chromium/services/data_decoder/public/mojom/BUILD.gn b/chromium/services/data_decoder/public/mojom/BUILD.gn index ee6240f24a1..59dac08b913 100644 --- a/chromium/services/data_decoder/public/mojom/BUILD.gn +++ b/chromium/services/data_decoder/public/mojom/BUILD.gn @@ -9,13 +9,13 @@ mojom("mojom") { "data_decoder_service.mojom", "image_decoder.mojom", "json_parser.mojom", - "web_bundle_parser.mojom", "web_bundler.mojom", "xml_parser.mojom", ] public_deps = [ ":mojom_resource_snapshot_for_web_bundle", + "//components/web_package/mojom", "//mojo/public/mojom/base", "//skia/public/mojom", "//ui/gfx/geometry/mojom", diff --git a/chromium/services/data_decoder/public/mojom/data_decoder_service.mojom b/chromium/services/data_decoder/public/mojom/data_decoder_service.mojom index 120ba37e7d4..77f2f1d421e 100644 --- a/chromium/services/data_decoder/public/mojom/data_decoder_service.mojom +++ b/chromium/services/data_decoder/public/mojom/data_decoder_service.mojom @@ -4,10 +4,10 @@ module data_decoder.mojom; +import "components/web_package/mojom/web_bundle_parser.mojom"; import "services/data_decoder/public/mojom/image_decoder.mojom"; import "services/data_decoder/public/mojom/json_parser.mojom"; import "services/data_decoder/public/mojom/web_bundler.mojom"; -import "services/data_decoder/public/mojom/web_bundle_parser.mojom"; import "services/data_decoder/public/mojom/xml_parser.mojom"; [EnableIf=is_chromeos] @@ -26,7 +26,7 @@ interface DataDecoderService { // Binds an interface which can be used to parse Web Bundles. BindWebBundleParserFactory( - pending_receiver<WebBundleParserFactory> receiver); + pending_receiver<web_package.mojom.WebBundleParserFactory> receiver); // Binds an interface which can be used to generate a Web Bundle. BindWebBundler(pending_receiver<WebBundler> receiver); diff --git a/chromium/services/data_decoder/public/mojom/web_bundle_parser.mojom b/chromium/services/data_decoder/public/mojom/web_bundle_parser.mojom deleted file mode 100644 index ca4c1be70a5..00000000000 --- a/chromium/services/data_decoder/public/mojom/web_bundle_parser.mojom +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -module data_decoder.mojom; - -import "mojo/public/mojom/base/file.mojom"; -import "url/mojom/url.mojom"; - -// Factory interface to create WebBundleParser for the passed |file| -// or |data_source| that provides application/webbundle data. -interface WebBundleParserFactory { - // Constructs a parser for the passed |file|. - GetParserForFile(pending_receiver<WebBundleParser> receiver, - mojo_base.mojom.File file); - - // Constructs a parser for the passed |data_source|. - GetParserForDataSource(pending_receiver<WebBundleParser> receiver, - pending_remote<BundleDataSource> data_source); -}; - -// Parser interface to obtain metadata and multiple responses from the bound -// application/webbundle data provided on the construction at the factory above. -interface WebBundleParser { - // Parses bundle's metadata. - ParseMetadata() => (BundleMetadata? Result, BundleMetadataParseError? error); - - // Parses a response from the range - // |[response_offset, response_offset + response_length)|. - ParseResponse(uint64 response_offset, - uint64 response_length) - => (BundleResponse? Result, BundleResponseParseError? error); -}; - -// Data source that provides application/webbundle data to the parser. -interface BundleDataSource { - // Reads up to |length| bytes starting with |offset|. Returns a non-null - // buffer shorter than |length| iff the end of the stream is reached. - Read(uint64 offset, uint64 length) => (array<uint8>? buffer); -}; - -enum BundleParseErrorType { - kParserInternalError, - kFormatError, - kVersionError, -}; - -struct BundleMetadataParseError { - BundleParseErrorType type; - url.mojom.Url fallback_url; - string message; -}; - -struct BundleResponseParseError { - BundleParseErrorType type; - string message; -}; - -// Corresponds to "bundle's metadata". -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#semantics-load-metadata -struct BundleMetadata { - url.mojom.Url primary_url; - map<url.mojom.Url, BundleIndexValue> requests; - url.mojom.Url manifest_url; - array<AugmentedCertificate> authorities; - array<VouchedSubset> vouched_subsets; -}; - -// Corresponds to the value type of "index" in the spec CDDL. -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#index-section -struct BundleIndexValue { - string variants_value; - array<BundleResponseLocation> response_locations; -}; - -// Offset (within the webbundle file) and length of a response. -struct BundleResponseLocation { - uint64 offset; - uint64 length; -}; - -// Corresponds to "augmented-certificate" in the spec CDDL. -// https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#cert-chain-format -struct AugmentedCertificate { - array<uint8> cert; - array<uint8>? ocsp; - array<uint8>? sct; -}; - -// Corresponds to the item type of "vouched-subsets" in the spec CDDL. -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#signatures-section -struct VouchedSubset { - uint64 authority; // An index in BundleMetadata::authorities. - array<uint8> sig; - array<uint8> raw_signed; - - SignedSubset parsed_signed; // Clients must verify |sig| before using this. -}; - -// Corresponds to "signed-subset" in the spec CDDL. -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#signatures-section -struct SignedSubset { - url.mojom.Url validity_url; - array<uint8> auth_sha256; - uint64 date; - uint64 expires; - map<url.mojom.Url, SubsetHashesValue> subset_hashes; -}; - -// Corresponds to the value type of "subset-hashes" in the spec CDDL. -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#signatures-section -struct SubsetHashesValue { - string variants_value; - array<ResourceIntegrity> resource_integrities; -}; - -// Corresponds to "resource-integrity" in the spec CDDL. -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#signatures-section -struct ResourceIntegrity { - array<uint8> header_sha256; - string payload_integrity_header; -}; - -// Contains response headers and payload location. -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-response -struct BundleResponse { - int32 response_code; - map<string, string> response_headers; - - // Payload offset and length within the webbundle file. - uint64 payload_offset; - uint64 payload_length; -}; diff --git a/chromium/services/data_decoder/web_bundle_parser.cc b/chromium/services/data_decoder/web_bundle_parser.cc deleted file mode 100644 index e354823ca31..00000000000 --- a/chromium/services/data_decoder/web_bundle_parser.cc +++ /dev/null @@ -1,1349 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/data_decoder/web_bundle_parser.h" - -#include <algorithm> - -#include "base/big_endian.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/containers/span.h" -#include "base/memory/weak_ptr.h" -#include "base/numerics/checked_math.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "components/cbor/reader.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "net/http/http_util.h" - -namespace data_decoder { - -namespace { - -// The maximum length of the CBOR item header (type and argument). -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#parse-type-argument -constexpr uint64_t kMaxCBORItemHeaderSize = 9; - -// The maximum size of the section-lengths CBOR item. -constexpr uint64_t kMaxSectionLengthsCBORSize = 8192; - -// The maximum size of a metadata section allowed in this implementation. -constexpr uint64_t kMaxMetadataSectionSize = 1 * 1024 * 1024; - -// The maximum size of the response header CBOR. -constexpr uint64_t kMaxResponseHeaderLength = 512 * 1024; - -// The initial buffer size for reading an item from the response section. -constexpr uint64_t kInitialBufferSizeForResponse = 4096; - -// Step 2. of -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-metadata -const uint8_t kBundleMagicBytes[] = { - 0x86, 0x48, 0xF0, 0x9F, 0x8C, 0x90, 0xF0, 0x9F, 0x93, 0xA6, -}; - -// Step 7. of -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-metadata -// We use an implementation-specific version string "b1\0\0". -const uint8_t kVersionB1MagicBytes[] = { - 0x44, 0x62, 0x31, 0x00, 0x00, -}; - -// Section names. -constexpr char kCriticalSection[] = "critical"; -constexpr char kIndexSection[] = "index"; -constexpr char kManifestSection[] = "manifest"; -constexpr char kResponsesSection[] = "responses"; -constexpr char kSignaturesSection[] = "signatures"; - -// https://tools.ietf.org/html/draft-ietf-cbor-7049bis-05#section-3.1 -enum class CBORType { - kByteString = 2, - kTextString = 3, - kArray = 4, -}; - -// A list of (section-name, length) pairs. -using SectionLengths = std::vector<std::pair<std::string, uint64_t>>; - -// A map from section name to (offset, length) pair. -using SectionOffsets = std::map<std::string, std::pair<uint64_t, uint64_t>>; - -bool IsMetadataSection(const std::string& name) { - return (name == kCriticalSection || name == kIndexSection || - name == kManifestSection || name == kSignaturesSection); -} - -// Parses a `section-lengths` CBOR item. -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-metadata -base::Optional<SectionLengths> ParseSectionLengths( - base::span<const uint8_t> data) { - cbor::Reader::DecoderError error; - base::Optional<cbor::Value> value = cbor::Reader::Read(data, &error); - if (!value.has_value() || !value->is_array()) - return base::nullopt; - - const cbor::Value::ArrayValue& array = value->GetArray(); - if (array.size() % 2 != 0) - return base::nullopt; - - SectionLengths result; - for (size_t i = 0; i < array.size(); i += 2) { - if (!array[i].is_string() || !array[i + 1].is_unsigned()) - return base::nullopt; - result.emplace_back(array[i].GetString(), array[i + 1].GetUnsigned()); - } - return result; -} - -struct ParsedHeaders { - base::flat_map<std::string, std::string> headers; - base::flat_map<std::string, std::string> pseudos; -}; - -// https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#cbor-headers -base::Optional<ParsedHeaders> ConvertCBORValueToHeaders( - const cbor::Value& headers_value) { - // Step 1. If item doesn’t match the headers rule in the above CDDL, return - // an error. - if (!headers_value.is_map()) - return base::nullopt; - - // Step 2. Let headers be a new header list ([FETCH]). - // Step 3. Let pseudos be an empty map ([INFRA]). - ParsedHeaders result; - - // Step 4. For each pair (name, value) in item: - for (const auto& item : headers_value.GetMap()) { - if (!item.first.is_bytestring() || !item.second.is_bytestring()) - return base::nullopt; - base::StringPiece name = item.first.GetBytestringAsString(); - base::StringPiece value = item.second.GetBytestringAsString(); - - // Step 4.1. If name contains any upper-case or non-ASCII characters, return - // an error. This matches the requirement in Section 8.1.2 of [RFC7540]. - if (!base::IsStringASCII(name) || - std::any_of(name.begin(), name.end(), base::IsAsciiUpper<char>)) - return base::nullopt; - - // Step 4.2. If name starts with a ‘:’: - if (!name.empty() && name[0] == ':') { - // Step 4.2.1. Assert: pseudos[name] does not exist, because CBOR maps - // cannot contain duplicate keys. - // This is ensured by cbor::Reader. - DCHECK(!result.pseudos.contains(name)); - // Step 4.2.2. Set pseudos[name] to value. - result.pseudos.insert( - std::make_pair(name.as_string(), value.as_string())); - // Step 4.3.3. Continue. - continue; - } - - // Step 4.3. If name or value doesn’t satisfy the requirements for a header - // in [FETCH], return an error. - if (!net::HttpUtil::IsValidHeaderName(name) || - !net::HttpUtil::IsValidHeaderValue(value)) - return base::nullopt; - - // Step 4.4. Assert: headers does not contain ([FETCH]) name, because CBOR - // maps cannot contain duplicate keys and an earlier step rejected - // upper-case bytes. - // This is ensured by cbor::Reader. - DCHECK(!result.headers.contains(name)); - - // Step 4.5. Append (name, value) to headers. - result.headers.insert(std::make_pair(name.as_string(), value.as_string())); - } - - // Step 5. Return (headers, pseudos). - return result; -} - -// A utility class for reading various values from input buffer. -class InputReader { - public: - explicit InputReader(base::span<const uint8_t> buf) : buf_(buf) {} - - uint64_t CurrentOffset() const { return current_offset_; } - size_t Size() const { return buf_.size(); } - - base::Optional<uint8_t> ReadByte() { - if (buf_.empty()) - return base::nullopt; - uint8_t byte = buf_[0]; - Advance(1); - return byte; - } - - template <typename T> - bool ReadBigEndian(T* out) { - auto bytes = ReadBytes(sizeof(T)); - if (!bytes) - return false; - base::ReadBigEndian(reinterpret_cast<const char*>(bytes->data()), out); - return true; - } - - base::Optional<base::span<const uint8_t>> ReadBytes(size_t n) { - if (buf_.size() < n) - return base::nullopt; - auto result = buf_.subspan(0, n); - Advance(n); - return result; - } - - base::Optional<base::StringPiece> ReadString(size_t n) { - auto bytes = ReadBytes(n); - if (!bytes) - return base::nullopt; - base::StringPiece str(reinterpret_cast<const char*>(bytes->data()), - bytes->size()); - if (!base::IsStringUTF8(str)) - return base::nullopt; - return str; - } - - // Parses the type and argument of a CBOR item from the input head. If parsed - // successfully and the type matches |expected_type|, returns the argument. - // Otherwise returns nullopt. - base::Optional<uint64_t> ReadCBORHeader(CBORType expected_type) { - auto pair = ReadTypeAndArgument(); - if (!pair || pair->first != expected_type) - return base::nullopt; - return pair->second; - } - - private: - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#parse-type-argument - base::Optional<std::pair<CBORType, uint64_t>> ReadTypeAndArgument() { - base::Optional<uint8_t> first_byte = ReadByte(); - if (!first_byte) - return base::nullopt; - - CBORType type = static_cast<CBORType>((*first_byte & 0xE0) / 0x20); - uint8_t b = *first_byte & 0x1F; - - if (b <= 23) - return std::make_pair(type, b); - if (b == 24) { - auto content = ReadByte(); - if (!content || *content < 24) - return base::nullopt; - return std::make_pair(type, *content); - } - if (b == 25) { - uint16_t content; - if (!ReadBigEndian(&content) || content >> 8 == 0) - return base::nullopt; - return std::make_pair(type, content); - } - if (b == 26) { - uint32_t content; - if (!ReadBigEndian(&content) || content >> 16 == 0) - return base::nullopt; - return std::make_pair(type, content); - } - if (b == 27) { - uint64_t content; - if (!ReadBigEndian(&content) || content >> 32 == 0) - return base::nullopt; - return std::make_pair(type, content); - } - return base::nullopt; - } - - void Advance(size_t n) { - DCHECK_LE(n, buf_.size()); - buf_ = buf_.subspan(n); - current_offset_ += n; - } - - base::span<const uint8_t> buf_; - uint64_t current_offset_ = 0; - - DISALLOW_COPY_AND_ASSIGN(InputReader); -}; - -GURL ParseExchangeURL(base::StringPiece str) { - if (!base::IsStringUTF8(str)) - return GURL(); - - GURL url(str); - if (!url.is_valid()) - return GURL(); - - // Exchange URL must not have a fragment or credentials. - if (url.has_ref() || url.has_username() || url.has_password()) - return GURL(); - - // For now, we allow only http: and https: schemes in Web Bundle URLs. - // TODO(crbug.com/966753): Revisit this once - // https://github.com/WICG/webpackage/issues/468 is resolved. - if (!url.SchemeIsHTTPOrHTTPS()) - return GURL(); - - return url; -} - -} // namespace - -class WebBundleParser::SharedBundleDataSource::Observer { - public: - Observer() {} - virtual ~Observer() {} - virtual void OnDisconnect() = 0; - - DISALLOW_COPY_AND_ASSIGN(Observer); -}; - -// A parser for bundle's metadata. This class owns itself and will self destruct -// after calling the ParseMetadataCallback. -class WebBundleParser::MetadataParser - : WebBundleParser::SharedBundleDataSource::Observer { - public: - MetadataParser(scoped_refptr<SharedBundleDataSource> data_source, - ParseMetadataCallback callback) - : data_source_(data_source), callback_(std::move(callback)) { - data_source_->AddObserver(this); - } - ~MetadataParser() override { data_source_->RemoveObserver(this); } - - void Start() { - // First, we will parse `magic`, `version`, and the CBOR header of - // `primary-url`. - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#top-level - const uint64_t length = sizeof(kBundleMagicBytes) + - sizeof(kVersionB1MagicBytes) + - kMaxCBORItemHeaderSize; - data_source_->Read(0, length, - base::BindOnce(&MetadataParser::ParseMagicBytes, - weak_factory_.GetWeakPtr())); - } - - private: - // Step 1-4 of - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-metadata - void ParseMagicBytes(const base::Optional<std::vector<uint8_t>>& data) { - if (!data) { - RunErrorCallbackAndDestroy("Error reading bundle magic bytes."); - return; - } - - // Step 1. "Seek to offset 0 in stream. Assert: this operation doesn't - // fail." - InputReader input(*data); - - // Step 2. "If reading 10 bytes from stream returns an error or doesn't - // return the bytes with hex encoding "86 48 F0 9F 8C 90 F0 9F 93 A6" - // (the CBOR encoding of the 6-item array initial byte and 8-byte bytestring - // initial byte, followed by 🌐📦 in UTF-8), return a "format error"." - const auto magic = input.ReadBytes(sizeof(kBundleMagicBytes)); - if (!magic || - !std::equal(magic->begin(), magic->end(), std::begin(kBundleMagicBytes), - std::end(kBundleMagicBytes))) { - RunErrorCallbackAndDestroy("Wrong magic bytes."); - return; - } - - // Step 3. "Let version be the result of reading 5 bytes from stream. If - // this is an error, return a "format error"." - const auto version = input.ReadBytes(sizeof(kVersionB1MagicBytes)); - if (!version) { - RunErrorCallbackAndDestroy("Cannot read version bytes."); - return; - } - if (!std::equal(version->begin(), version->end(), - std::begin(kVersionB1MagicBytes), - std::end(kVersionB1MagicBytes))) { - version_mismatch_ = true; - // Continue parsing until Step 7 where we get a fallback URL, and - // then return "version error" with the fallback URL. - } - - // Step 4. "Let urlType and urlLength be the result of reading the type and - // argument of a CBOR item from stream (Section 3.5.3). If this is an error - // or urlType is not 3 (a CBOR text string), return a "format error"." - const auto url_length = input.ReadCBORHeader(CBORType::kTextString); - if (!url_length) { - RunErrorCallbackAndDestroy("Cannot parse the size of fallback URL."); - return; - } - - // In the next step, we will parse the content of `primary-url`, - // `section-lengths`, and the CBOR header of `sections`. - const uint64_t length = - *url_length + kMaxSectionLengthsCBORSize + kMaxCBORItemHeaderSize * 2; - data_source_->Read(input.CurrentOffset(), length, - base::BindOnce(&MetadataParser::ParseBundleHeader, - weak_factory_.GetWeakPtr(), *url_length, - input.CurrentOffset())); - } - - // Step 5-21 of - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-metadata - void ParseBundleHeader(uint64_t url_length, - uint64_t offset_in_stream, - const base::Optional<std::vector<uint8_t>>& data) { - if (!data) { - RunErrorCallbackAndDestroy("Error reading bundle header."); - return; - } - InputReader input(*data); - - // Step 5. "Let fallbackUrlBytes be the result of reading urlLength bytes - // from stream. If this is an error, return a "format error"." - const auto fallback_url_string = input.ReadString(url_length); - if (!fallback_url_string) { - RunErrorCallbackAndDestroy("Cannot read fallback URL."); - return; - } - - // Step 6. "Let fallbackUrl be the result of parsing ([URL]) the UTF-8 - // decoding of fallbackUrlBytes with no base URL. If either the UTF-8 - // decoding or parsing fails, return a "format error"." - // For now, we enforce the same restriction as exchages' request URL. - // TODO(crbug.com/966753): Revisit URL requirements here once - // https://github.com/WICG/webpackage/issues/469 is resolved. - GURL fallback_url = ParseExchangeURL(*fallback_url_string); - if (!fallback_url.is_valid()) { - RunErrorCallbackAndDestroy("Cannot parse fallback URL."); - return; - } - - // "Note: From this point forward, errors also include the fallback URL to - // help clients recover." - fallback_url_ = std::move(fallback_url); - - // Step 7. "If version does not have the hex encoding "44 31 00 00 00" (the - // CBOR encoding of a 4-byte byte string holding an ASCII "1" followed by - // three 0 bytes), return a "version error" with fallbackUrl. " - // Note: We use an implementation-specific version string - // kVersionB1MagicBytes. - if (version_mismatch_) { - RunErrorCallbackAndDestroy( - "Version error: this implementation only supports " - "bundle format of version b1.", - mojom::BundleParseErrorType::kVersionError); - return; - } - - // Step 8. "Let sectionLengthsLength be the result of getting the length of - // the CBOR bytestring header from stream (Section 3.5.2). If this is an - // error, return a "format error" with fallbackUrl." - const auto section_lengths_length = - input.ReadCBORHeader(CBORType::kByteString); - if (!section_lengths_length) { - RunErrorCallbackAndDestroy("Cannot parse the size of section-lengths."); - return; - } - // Step 9. "If sectionLengthsLength is 8192 (8*1024) or greater, return a - // "format error" with fallbackUrl." - if (*section_lengths_length >= kMaxSectionLengthsCBORSize) { - RunErrorCallbackAndDestroy( - "The section-lengths CBOR must be smaller than 8192 bytes."); - return; - } - - // Step 10. "Let sectionLengthsBytes be the result of reading - // sectionLengthsLength bytes from stream. If sectionLengthsBytes is an - // error, return a "format error" with fallbackUrl." - const auto section_lengths_bytes = input.ReadBytes(*section_lengths_length); - if (!section_lengths_bytes) { - RunErrorCallbackAndDestroy("Cannot read section-lengths."); - return; - } - - // Step 11. "Let sectionLengths be the result of parsing one CBOR item - // (Section 3.5) from sectionLengthsBytes, matching the section-lengths - // rule in the CDDL ([I-D.ietf-cbor-cddl]) above. If sectionLengths is an - // error, return a "format error" with fallbackUrl." - const auto section_lengths = ParseSectionLengths(*section_lengths_bytes); - if (!section_lengths) { - RunErrorCallbackAndDestroy("Cannot parse section-lengths."); - return; - } - - // Step 12. "Let (sectionsType, numSections) be the result of parsing the - // type and argument of a CBOR item from stream (Section 3.5.3)." - const auto num_sections = input.ReadCBORHeader(CBORType::kArray); - if (!num_sections) { - RunErrorCallbackAndDestroy("Cannot parse the number of sections."); - return; - } - - // Step 13. "If sectionsType is not 4 (a CBOR array) or numSections is not - // half of the length of sectionLengths, return a "format error" with - // fallbackUrl." - if (*num_sections != section_lengths->size()) { - RunErrorCallbackAndDestroy("Unexpected number of sections."); - return; - } - - // Step 14. "Let sectionsStart be the current offset within stream." - // Note: This doesn't exceed |size_|. - const uint64_t sections_start = offset_in_stream + input.CurrentOffset(); - - // Step 15. "Let knownSections be the subset of the Section 6.2 that this - // client has implemented." - // Step 16. "Let ignoredSections be an empty set." - - // This implementation doesn't use knownSections nor ignoredSections. - - // Step 17. "Let sectionOffsets be an empty map ([INFRA]) from section names - // to (offset, length) pairs. These offsets are relative to the start of - // stream." - - // |section_offsets_| is defined as a class member field. - - // Step 18. "Let currentOffset be sectionsStart." - uint64_t current_offset = sections_start; - - // Step 19. "For each ("name", length) pair of adjacent elements in - // sectionLengths:" - for (const auto& pair : *section_lengths) { - const std::string& name = pair.first; - const uint64_t length = pair.second; - // Step 19.1. "If "name"'s specification in knownSections says not to - // process other sections, add those sections' names to ignoredSections." - - // There're no such sections at the moment. - - // Step 19.2. "If sectionOffsets["name"] exists, return a "format error" - // with fallbackUrl. That is, duplicate sections are forbidden." - // Step 19.3. "Set sectionOffsets["name"] to (currentOffset, length)." - bool added = section_offsets_ - .insert(std::make_pair( - name, std::make_pair(current_offset, length))) - .second; - if (!added) { - RunErrorCallbackAndDestroy("Duplicated section."); - return; - } - - // Step 19.4. "Set currentOffset to currentOffset + length." - if (!base::CheckAdd(current_offset, length) - .AssignIfValid(¤t_offset)) { - RunErrorCallbackAndDestroy( - "Integer overflow calculating section offsets."); - return; - } - } - - // Step 20. "If the "responses" section is not last in sectionLengths, - // return a "format error" with fallbackUrl. This allows a streaming parser - // to assume that it'll know the requests by the time their responses - // arrive." - if (section_lengths->empty() || - section_lengths->back().first != kResponsesSection) { - RunErrorCallbackAndDestroy( - "Responses section is not the last in section-lengths."); - return; - } - - // Step 21. "Let metadata be a map ([INFRA]) initially containing the single - // key/value pair "primaryUrl"/fallbackUrl." - metadata_ = mojom::BundleMetadata::New(); - metadata_->primary_url = fallback_url_; - - ReadMetadataSections(section_offsets_.begin()); - } - - // Step 22-25 of - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-metadata - void ReadMetadataSections(SectionOffsets::const_iterator section_iter) { - // Step 22. "For each "name" -> (offset, length) triple in sectionOffsets:" - for (; section_iter != section_offsets_.end(); ++section_iter) { - const auto& name = section_iter->first; - // Step 22.1. "If "name" isn't in knownSections, continue to the next - // triple." - // Step 22.2. "If "name"'s Metadata field (Section 6.2) is "No", continue - // to the next triple." - if (!IsMetadataSection(name)) - continue; - - // Step 22.3. "If "name" is in ignoredSections, continue to the next - // triple." - // In the current spec, ignoredSections is always empty. - - const uint64_t section_offset = section_iter->second.first; - const uint64_t section_length = section_iter->second.second; - if (section_length > kMaxMetadataSectionSize) { - RunErrorCallbackAndDestroy( - "Metadata sections larger than 1MB are not supported."); - return; - } - - data_source_->Read(section_offset, section_length, - base::BindOnce(&MetadataParser::ParseMetadataSection, - weak_factory_.GetWeakPtr(), - section_iter, section_length)); - // This loop will be resumed by ParseMetadataSection(). - return; - } - - // Step 23. "Assert: metadata has an entry with the key "primaryUrl"." - DCHECK(!metadata_->primary_url.is_empty()); - - // Step 24. "If metadata doesn't have an entry with the key "requests", - // return a "format error" with fallbackUrl." - if (metadata_->requests.empty()) { - RunErrorCallbackAndDestroy("Bundle must have an index section."); - return; - } - - // Step 25. "Return metadata." - RunSuccessCallbackAndDestroy(); - } - - void ParseMetadataSection(SectionOffsets::const_iterator section_iter, - uint64_t expected_data_length, - const base::Optional<std::vector<uint8_t>>& data) { - if (!data || data->size() != expected_data_length) { - RunErrorCallbackAndDestroy("Error reading section content."); - return; - } - - // Parse the section contents as a CBOR item. - cbor::Reader::DecoderError error; - base::Optional<cbor::Value> section_value = - cbor::Reader::Read(*data, &error); - if (!section_value) { - RunErrorCallbackAndDestroy( - std::string("Error parsing section contents as CBOR: ") + - cbor::Reader::ErrorCodeToString(error)); - return; - } - - // Step 22.6. "Follow "name"'s specification from knownSections to process - // the section, passing sectionContents, stream, sectionOffsets, and - // metadata. If this returns an error, return a "format error" with - // fallbackUrl." - const auto& name = section_iter->first; - // Note: Parse*Section() delete |this| on failure. - if (name == kIndexSection) { - if (!ParseIndexSection(*section_value)) - return; - } else if (name == kManifestSection) { - if (!ParseManifestSection(*section_value)) - return; - } else if (name == kSignaturesSection) { - if (!ParseSignaturesSection(*section_value)) - return; - } else if (name == kCriticalSection) { - if (!ParseCriticalSection(*section_value)) - return; - } else { - NOTREACHED(); - } - // Resume the loop of Step 22. - ReadMetadataSections(++section_iter); - } - - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#index-section - bool ParseIndexSection(const cbor::Value& section_value) { - // Step 1. "Let index be the result of parsing sectionContents as a CBOR - // item matching the index rule in the above CDDL (Section 3.5). If index is - // an error, return an error." - if (!section_value.is_map()) { - RunErrorCallbackAndDestroy("Index section must be a map."); - return false; - } - - // Step 2. "Let requests be an initially-empty map ([INFRA]) from URLs to - // response descriptions, each of which is either a single - // location-in-stream value or a pair of a Variants header field value - // ([I-D.ietf-httpbis-variants]) and a map from that value's possible - // Variant-Keys to location-in-stream values, as described in Section 2.2." - base::flat_map<GURL, mojom::BundleIndexValuePtr> requests; - - // Step 3. "Let MakeRelativeToStream be a function that takes a - // location-in-responses value (offset, length) and returns a - // ResponseMetadata struct or error by running the following - // sub-steps:" - // The logic of MakeRelativeToStream is inlined at the callsite below. - auto responses_section = section_offsets_.find(kResponsesSection); - DCHECK(responses_section != section_offsets_.end()); - const uint64_t responses_section_offset = responses_section->second.first; - const uint64_t responses_section_length = responses_section->second.second; - - // Step 4. "For each (url, responses) entry in the index map:" - for (const auto& item : section_value.GetMap()) { - if (!item.first.is_string()) { - RunErrorCallbackAndDestroy("Index section: key must be a string."); - return false; - } - if (!item.second.is_array()) { - RunErrorCallbackAndDestroy("Index section: value must be an array."); - return false; - } - const std::string& url = item.first.GetString(); - const cbor::Value::ArrayValue& responses_array = item.second.GetArray(); - - // Step 4.1. "Let parsedUrl be the result of parsing ([URL]) url with no - // base URL." - GURL parsed_url = ParseExchangeURL(url); - - // Step 4.2. "If parsedUrl is a failure, its fragment is not null, or it - // includes credentials, return an error." - if (!parsed_url.is_valid()) { - RunErrorCallbackAndDestroy("Index section: exchange URL is not valid."); - return false; - } - - // Step 4.3. "If the first element of responses is the empty string:" - if (responses_array.empty() || !responses_array[0].is_bytestring()) { - RunErrorCallbackAndDestroy( - "Index section: the first element of responses array must be a " - "bytestring."); - return false; - } - base::StringPiece variants_value = - responses_array[0].GetBytestringAsString(); - if (variants_value.empty()) { - // Step 4.3.1. "If the length of responses is not 3 (i.e. there is more - // than one location-in-responses in responses), return an error." - if (responses_array.size() != 3) { - RunErrorCallbackAndDestroy( - "Index section: unexpected size of responses array."); - return false; - } - } else { - // Step 4.4. "Otherwise:" - // TODO(crbug.com/969596): Parse variants_value to compute the number of - // variantKeys, and check that responses_array has - // (2 * #variantKeys + 1) elements. - if (responses_array.size() < 3 || responses_array.size() % 2 != 1) { - RunErrorCallbackAndDestroy( - "Index section: unexpected size of responses array."); - return false; - } - } - // Instead of constructing a map from Variant-Keys to location-in-stream, - // this implementation just returns the responses array's structure as - // a BundleIndexValue. - std::vector<mojom::BundleResponseLocationPtr> response_locations; - for (size_t i = 1; i < responses_array.size(); i += 2) { - if (!responses_array[i].is_unsigned() || - !responses_array[i + 1].is_unsigned()) { - RunErrorCallbackAndDestroy( - "Index section: offset and length values must be unsigned."); - return false; - } - uint64_t offset = responses_array[i].GetUnsigned(); - uint64_t length = responses_array[i + 1].GetUnsigned(); - - // MakeRelativeToStream (Step 3.) is inlined here. - // Step 3.1. "If offset + length is larger than - // sectionOffsets["responses"].length, return an error." - uint64_t response_end; - if (!base::CheckAdd(offset, length).AssignIfValid(&response_end) || - response_end > responses_section_length) { - RunErrorCallbackAndDestroy("Index section: response out of range."); - return false; - } - // Step 3.2. "Otherwise, return a ResponseMetadata struct whose offset - // is sectionOffsets["responses"].offset + offset and whose length is - // length." - - // This doesn't wrap because (offset <= responses_section_length) and - // (responses_section_offset + responses_section_length) doesn't wrap. - uint64_t offset_within_stream = responses_section_offset + offset; - - response_locations.push_back( - mojom::BundleResponseLocation::New(offset_within_stream, length)); - } - requests.insert(std::make_pair( - parsed_url, - mojom::BundleIndexValue::New(variants_value.as_string(), - std::move(response_locations)))); - } - - // Step 5. "Set metadata["requests"] to requests." - metadata_->requests = std::move(requests); - return true; - } - - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#manifest-section - bool ParseManifestSection(const cbor::Value& section_value) { - // Step 1. "Let urlString be the result of parsing sectionContents as a CBOR - // item matching the above manifest rule (Section 3.5). If urlString is an - // error, return that error." - if (!section_value.is_string()) { - RunErrorCallbackAndDestroy("Manifest section must be a string."); - return false; - } - // Step 2. "Let url be the result of parsing ([URL]) urlString with no base - // URL." - GURL parsed_url = ParseExchangeURL(section_value.GetString()); - - // Step 3. "If url is a failure, its fragment is not null, or it includes - // credentials, return an error." - if (!parsed_url.is_valid()) { - RunErrorCallbackAndDestroy("Manifest URL is not a valid exchange URL."); - return false; - } - // Step 4. "Set metadata["manifest"] to url." - metadata_->manifest_url = std::move(parsed_url); - return true; - } - - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#signatures-section - bool ParseSignaturesSection(const cbor::Value& section_value) { - // Step 1. "Let signatures be the result of parsing sectionContents as a - // CBOR item matching the signatures rule in the above CDDL (Section 3.5)." - if (!section_value.is_array() || section_value.GetArray().size() != 2) { - RunErrorCallbackAndDestroy( - "Signatures section must be an array of size 2."); - return false; - } - const cbor::Value& authorities_value = section_value.GetArray()[0]; - const cbor::Value& vouched_subsets_value = section_value.GetArray()[1]; - - if (!authorities_value.is_array()) { - RunErrorCallbackAndDestroy("Authorities must be an array."); - return false; - } - std::vector<mojom::AugmentedCertificatePtr> authorities; - for (const cbor::Value& value : authorities_value.GetArray()) { - // ParseAugmentedCertificate deletes |this| on failure. - auto authority = ParseAugmentedCertificate(value); - if (!authority) - return false; - authorities.push_back(std::move(authority)); - } - - if (!vouched_subsets_value.is_array()) { - RunErrorCallbackAndDestroy("Vouched-subsets must be an array."); - return false; - } - std::vector<mojom::VouchedSubsetPtr> vouched_subsets; - for (const cbor::Value& value : vouched_subsets_value.GetArray()) { - if (!value.is_map()) { - RunErrorCallbackAndDestroy( - "An element of vouched-subsets must be a map."); - return false; - } - const cbor::Value::MapValue& item_map = value.GetMap(); - - auto* authority_value = Lookup(item_map, "authority"); - if (!authority_value || !authority_value->is_unsigned()) { - RunErrorCallbackAndDestroy( - "authority is not found in vouched-subsets map, or not an " - "unsigned."); - return false; - } - int64_t authority = authority_value->GetUnsigned(); - - auto* sig_value = Lookup(item_map, "sig"); - if (!sig_value || !sig_value->is_bytestring()) { - RunErrorCallbackAndDestroy( - "sig is not found in vouched-subsets map, or not a bytestring."); - return false; - } - const cbor::Value::BinaryValue& sig = sig_value->GetBytestring(); - - auto* signed_value = Lookup(item_map, "signed"); - if (!signed_value || !signed_value->is_bytestring()) { - RunErrorCallbackAndDestroy( - "signed is not found in vouched-subsets map, or not a bytestring."); - return false; - } - const cbor::Value::BinaryValue& raw_signed = - signed_value->GetBytestring(); - - // ParseSignedSubset deletes |this| on failure. - auto parsed_signed = ParseSignedSubset(raw_signed); - if (!parsed_signed) - return false; - - vouched_subsets.push_back(mojom::VouchedSubset::New( - authority, sig, raw_signed, std::move(parsed_signed))); - } - - // Step 2. "Set metadata["authorities"] to the list of authorities in the - // first element of the signatures array." - metadata_->authorities = std::move(authorities); - - // Step 3. "Set metadata["vouched-subsets"] to the second element of the - // signatures array." - metadata_->vouched_subsets = std::move(vouched_subsets); - - return true; - } - - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#critical-section - bool ParseCriticalSection(const cbor::Value& section_value) { - // Step 1. "Let critical be the result of parsing sectionContents as a CBOR - // item matching the above critical rule (Section 3.5). If critical is an - // error, return that error." - if (!section_value.is_array()) { - RunErrorCallbackAndDestroy("Critical section must be an array."); - return false; - } - // Step 2. "For each value sectionName in the critical list, if the client - // has not implemented sections named sectionName, return an error." - for (const cbor::Value& elem : section_value.GetArray()) { - if (!elem.is_string()) { - RunErrorCallbackAndDestroy( - "Non-string element in the critical section."); - return false; - } - const auto& section_name = elem.GetString(); - if (!IsMetadataSection(section_name) && - section_name != kResponsesSection) { - RunErrorCallbackAndDestroy("Unknown critical section."); - return false; - } - } - return true; - } - - // https://wicg.github.io/webpackage/draft-yasskin-http-origin-signed-responses.html#cert-chain-format - mojom::AugmentedCertificatePtr ParseAugmentedCertificate( - const cbor::Value& value) { - if (!value.is_map()) { - RunErrorCallbackAndDestroy("augmented-certificate must be a map."); - return nullptr; - } - const cbor::Value::MapValue& item_map = value.GetMap(); - mojom::AugmentedCertificatePtr authority = - mojom::AugmentedCertificate::New(); - - auto* cert_value = Lookup(item_map, "cert"); - if (!cert_value || !cert_value->is_bytestring()) { - RunErrorCallbackAndDestroy( - "cert is not found in augmented-certificate, or not a bytestring."); - return nullptr; - } - authority->cert = cert_value->GetBytestring(); - - if (auto* ocsp_value = Lookup(item_map, "ocsp")) { - if (!ocsp_value->is_bytestring()) { - RunErrorCallbackAndDestroy("ocsp is not a bytestring."); - return nullptr; - } - authority->ocsp = ocsp_value->GetBytestring(); - } - - if (auto* sct_value = Lookup(item_map, "sct")) { - if (!sct_value->is_bytestring()) { - RunErrorCallbackAndDestroy("sct is not a bytestring."); - return nullptr; - } - authority->sct = sct_value->GetBytestring(); - } - return authority; - } - - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#signatures-section - mojom::SignedSubsetPtr ParseSignedSubset( - const cbor::Value::BinaryValue& signed_bytes) { - // Parse |signed_bytes| as a CBOR item. - cbor::Reader::DecoderError error; - base::Optional<cbor::Value> value = - cbor::Reader::Read(signed_bytes, &error); - if (!value) { - RunErrorCallbackAndDestroy( - std::string("Error parsing signed bytes as CBOR: ") + - cbor::Reader::ErrorCodeToString(error)); - return nullptr; - } - - if (!value->is_map()) { - RunErrorCallbackAndDestroy("signed-subset must be a CBOR map"); - return nullptr; - } - const cbor::Value::MapValue& value_map = value->GetMap(); - - auto* validity_url_value = Lookup(value_map, "validity-url"); - if (!validity_url_value || !validity_url_value->is_string()) { - RunErrorCallbackAndDestroy( - "validity-url is not found in signed-subset, or not a string."); - return nullptr; - } - // TODO(crbug.com/966753): Revisit this once requirements for validity URL - // are speced. - GURL validity_url(validity_url_value->GetString()); - if (!validity_url.is_valid()) { - RunErrorCallbackAndDestroy("Cannot parse validity-url."); - return nullptr; - } - - auto* auth_sha256_value = Lookup(value_map, "auth-sha256"); - if (!auth_sha256_value || !auth_sha256_value->is_bytestring()) { - RunErrorCallbackAndDestroy( - "auth-sha256 is not found in signed-subset, or not a bytestring."); - return nullptr; - } - auto auth_sha256 = auth_sha256_value->GetBytestring(); - - auto* date_value = Lookup(value_map, "date"); - if (!date_value || !date_value->is_unsigned()) { - RunErrorCallbackAndDestroy( - "date is not found in signed-subset, or not an unsigned."); - return nullptr; - } - auto date = date_value->GetUnsigned(); - - auto* expires_value = Lookup(value_map, "expires"); - if (!expires_value || !expires_value->is_unsigned()) { - RunErrorCallbackAndDestroy( - "expires is not found in signed-subset, or not an unsigned."); - return nullptr; - } - auto expires = expires_value->GetUnsigned(); - - auto* subset_hashes_value = Lookup(value_map, "subset-hashes"); - if (!subset_hashes_value || !subset_hashes_value->is_map()) { - RunErrorCallbackAndDestroy( - "subset-hashes is not found in signed-subset, or not a map."); - return nullptr; - } - base::flat_map<GURL, mojom::SubsetHashesValuePtr> subset_hashes; - - for (const auto& item : subset_hashes_value->GetMap()) { - if (!item.first.is_string()) { - RunErrorCallbackAndDestroy("subset-hashes: key must be a string."); - return nullptr; - } - if (!item.second.is_array()) { - RunErrorCallbackAndDestroy("subset-hashes: value must be an array."); - return nullptr; - } - const std::string& url = item.first.GetString(); - const cbor::Value::ArrayValue& value_array = item.second.GetArray(); - - GURL parsed_url = ParseExchangeURL(url); - if (!parsed_url.is_valid()) { - RunErrorCallbackAndDestroy("subset-hashes: exchange URL is not valid."); - return nullptr; - } - - if (value_array.empty() || !value_array[0].is_bytestring()) { - RunErrorCallbackAndDestroy( - "subset-hashes: the first element of array must be a bytestring."); - return nullptr; - } - base::StringPiece variants_value = value_array[0].GetBytestringAsString(); - if (value_array.size() < 3 || value_array.size() % 2 != 1) { - RunErrorCallbackAndDestroy( - "subset-hashes: unexpected size of value array."); - return nullptr; - } - std::vector<mojom::ResourceIntegrityPtr> resource_integrities; - for (size_t i = 1; i < value_array.size(); i += 2) { - if (!value_array[i].is_bytestring()) { - RunErrorCallbackAndDestroy( - "subset-hashes: header-sha256 must be a byte string."); - return nullptr; - } - if (!value_array[i + 1].is_string()) { - RunErrorCallbackAndDestroy( - "subset-hashes: payload-integrity-header must be a string."); - return nullptr; - } - resource_integrities.push_back(mojom::ResourceIntegrity::New( - value_array[i].GetBytestring(), value_array[i + 1].GetString())); - } - subset_hashes.insert(std::make_pair( - parsed_url, - mojom::SubsetHashesValue::New(variants_value.as_string(), - std::move(resource_integrities)))); - } - - return mojom::SignedSubset::New(validity_url, auth_sha256, date, expires, - std::move(subset_hashes)); - } - - // Returns nullptr if |key| is not in |map|. - const cbor::Value* Lookup(const cbor::Value::MapValue& map, const char* key) { - auto iter = map.find(cbor::Value(key)); - if (iter == map.end()) - return nullptr; - return &iter->second; - } - - void RunSuccessCallbackAndDestroy() { - std::move(callback_).Run(std::move(metadata_), nullptr); - delete this; - } - - void RunErrorCallbackAndDestroy( - const std::string& message, - mojom::BundleParseErrorType error_type = - mojom::BundleParseErrorType::kFormatError) { - mojom::BundleMetadataParseErrorPtr err = - mojom::BundleMetadataParseError::New(error_type, fallback_url_, - message); - std::move(callback_).Run(nullptr, std::move(err)); - delete this; - } - - // Implements SharedBundleDataSource::Observer. - void OnDisconnect() override { - RunErrorCallbackAndDestroy("Data source disconnected."); - } - - scoped_refptr<SharedBundleDataSource> data_source_; - ParseMetadataCallback callback_; - bool version_mismatch_ = false; - GURL fallback_url_; - SectionOffsets section_offsets_; - mojom::BundleMetadataPtr metadata_; - - base::WeakPtrFactory<MetadataParser> weak_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(MetadataParser); -}; - -// A parser for reading single item from the responses section. This class owns -// itself and will self destruct after calling the ParseResponseCallback. -class WebBundleParser::ResponseParser - : public WebBundleParser::SharedBundleDataSource::Observer { - public: - ResponseParser(scoped_refptr<SharedBundleDataSource> data_source, - uint64_t response_offset, - uint64_t response_length, - WebBundleParser::ParseResponseCallback callback) - : data_source_(data_source), - response_offset_(response_offset), - response_length_(response_length), - callback_(std::move(callback)) { - data_source_->AddObserver(this); - } - ~ResponseParser() override { data_source_->RemoveObserver(this); } - - void Start(uint64_t buffer_size = kInitialBufferSizeForResponse) { - const uint64_t length = std::min(response_length_, buffer_size); - data_source_->Read(response_offset_, length, - base::BindOnce(&ResponseParser::ParseResponseHeader, - weak_factory_.GetWeakPtr(), length)); - } - - private: - // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#load-response - void ParseResponseHeader(uint64_t expected_data_length, - const base::Optional<std::vector<uint8_t>>& data) { - // Step 1. "Seek to offset requestMetadata.offset in stream. If this fails, - // return an error." - if (!data || data->size() != expected_data_length) { - RunErrorCallbackAndDestroy("Error reading response header."); - return; - } - InputReader input(*data); - - // Step 2. "Read 1 byte from stream. If this is an error or isn't 0x82, - // return an error." - auto num_elements = input.ReadCBORHeader(CBORType::kArray); - if (!num_elements || *num_elements != 2) { - RunErrorCallbackAndDestroy("Array size of response must be 2."); - return; - } - - // Step 3. "Let headerLength be the result of getting the length of a CBOR - // bytestring header from stream (Section 3.5.2). If headerLength is an - // error, return that error." - auto header_length = input.ReadCBORHeader(CBORType::kByteString); - if (!header_length) { - RunErrorCallbackAndDestroy("Cannot parse response header length."); - return; - } - - // Step 4. "If headerLength is 524288 (512*1024) or greater, return an - // error." - if (*header_length >= kMaxResponseHeaderLength) { - RunErrorCallbackAndDestroy("Response header is too big."); - return; - } - - // If we don't have enough data for the headers and the CBOR header of the - // payload, re-read with a larger buffer size. - const uint64_t required_buffer_size = std::min( - input.CurrentOffset() + *header_length + kMaxCBORItemHeaderSize, - response_length_); - if (data->size() < required_buffer_size) { - DVLOG(1) << "Re-reading response header with a buffer of size " - << required_buffer_size; - Start(required_buffer_size); - return; - } - - // Step 5. "Let headerCbor be the result of reading headerLength bytes from - // stream and parsing a CBOR item from them matching the headers CDDL rule. - // If either the read or parse returns an error, return that error." - auto headers_bytes = input.ReadBytes(*header_length); - if (!headers_bytes) { - RunErrorCallbackAndDestroy("Cannot read response headers."); - return; - } - cbor::Reader::DecoderError error; - base::Optional<cbor::Value> headers_value = - cbor::Reader::Read(*headers_bytes, &error); - if (!headers_value) { - RunErrorCallbackAndDestroy("Cannot parse response headers."); - return; - } - - // Step 6. "Let (headers, pseudos) be the result of converting headerCbor - // to a header list and pseudoheaders using the algorithm in Section 3.6. - // If this returns an error, return that error." - auto parsed_headers = ConvertCBORValueToHeaders(*headers_value); - if (!parsed_headers) { - RunErrorCallbackAndDestroy("Cannot parse response headers."); - return; - } - - // Step 7. "If pseudos does not have a key named ':status' or its size - // isn't 1, return an error." - const auto pseudo_status = parsed_headers->pseudos.find(":status"); - if (parsed_headers->pseudos.size() != 1 || - pseudo_status == parsed_headers->pseudos.end()) { - RunErrorCallbackAndDestroy( - "Response headers map must have exactly one pseudo-header, :status."); - return; - } - - // Step 8. "If pseudos[':status'] isn't exactly 3 ASCII decimal digits, - // return an error." - int status; - const auto& status_str = pseudo_status->second; - if (status_str.size() != 3 || - !std::all_of(status_str.begin(), status_str.end(), - base::IsAsciiDigit<char>) || - !base::StringToInt(status_str, &status)) { - RunErrorCallbackAndDestroy(":status must be 3 ASCII decimal digits."); - return; - } - - // Step 9. "Let payloadLength be the result of getting the length of a CBOR - // bytestring header from stream (Section 3.5.2). If payloadLength is an - // error, return that error." - auto payload_length = input.ReadCBORHeader(CBORType::kByteString); - if (!payload_length) { - RunErrorCallbackAndDestroy("Cannot parse response payload length."); - return; - } - - // Step 10. "If payloadLength is greater than 0 and headers does not contain - // a Content-Type header, return an error." - if (*payload_length > 0 && - !parsed_headers->headers.contains("content-type")) { - RunErrorCallbackAndDestroy( - "Non-empty response must have a content-type header."); - return; - } - - // Step 11. "If stream.currentOffset + payloadLength != - // requestMetadata.offset + requestMetadata.length, return an error." - if (input.CurrentOffset() + *payload_length != response_length_) { - RunErrorCallbackAndDestroy("Unexpected payload length."); - return; - } - - // Step 12. "Let body be a new body ([FETCH]) whose stream is a tee'd copy - // of stream starting at the current offset and ending after payloadLength - // bytes." - - // Step 13. "Let response be a new response ([FETCH]) whose: - // - Url list is request's url list, - // - status is pseudos[':status'], - // - header list is headers, and - // - body is body." - mojom::BundleResponsePtr response = mojom::BundleResponse::New(); - response->response_code = status; - response->response_headers = std::move(parsed_headers->headers); - response->payload_offset = response_offset_ + input.CurrentOffset(); - response->payload_length = *payload_length; - RunSuccessCallbackAndDestroy(std::move(response)); - } - - void RunSuccessCallbackAndDestroy(mojom::BundleResponsePtr response) { - std::move(callback_).Run(std::move(response), nullptr); - delete this; - } - - void RunErrorCallbackAndDestroy( - const std::string& message, - mojom::BundleParseErrorType error_type = - mojom::BundleParseErrorType::kFormatError) { - std::move(callback_).Run( - nullptr, mojom::BundleResponseParseError::New(error_type, message)); - delete this; - } - - // Implements SharedBundleDataSource::Observer. - void OnDisconnect() override { - RunErrorCallbackAndDestroy("Data source disconnected."); - } - - scoped_refptr<SharedBundleDataSource> data_source_; - uint64_t response_offset_; - uint64_t response_length_; - ParseResponseCallback callback_; - - base::WeakPtrFactory<ResponseParser> weak_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(ResponseParser); -}; - -WebBundleParser::SharedBundleDataSource::SharedBundleDataSource( - mojo::PendingRemote<mojom::BundleDataSource> pending_data_source) - : data_source_(std::move(pending_data_source)) { - data_source_.set_disconnect_handler(base::BindOnce( - &SharedBundleDataSource::OnDisconnect, base::Unretained(this))); -} - -void WebBundleParser::SharedBundleDataSource::AddObserver(Observer* observer) { - DCHECK(observers_.end() == observers_.find(observer)); - observers_.insert(observer); -} - -void WebBundleParser::SharedBundleDataSource::RemoveObserver( - Observer* observer) { - auto it = observers_.find(observer); - DCHECK(observers_.end() != it); - observers_.erase(it); -} - -WebBundleParser::SharedBundleDataSource::~SharedBundleDataSource() = default; - -void WebBundleParser::SharedBundleDataSource::OnDisconnect() { - for (auto* observer : observers_) - observer->OnDisconnect(); -} - -void WebBundleParser::SharedBundleDataSource::Read(uint64_t offset, - uint64_t length, - ReadCallback callback) { - data_source_->Read(offset, length, std::move(callback)); -} - -WebBundleParser::WebBundleParser( - mojo::PendingReceiver<mojom::WebBundleParser> receiver, - mojo::PendingRemote<mojom::BundleDataSource> data_source) - : receiver_(this, std::move(receiver)), - data_source_(base::MakeRefCounted<SharedBundleDataSource>( - std::move(data_source))) { - receiver_.set_disconnect_handler(base::BindOnce( - &base::DeletePointer<WebBundleParser>, base::Unretained(this))); -} - -WebBundleParser::~WebBundleParser() = default; - -void WebBundleParser::ParseMetadata(ParseMetadataCallback callback) { - MetadataParser* parser = - new MetadataParser(data_source_, std::move(callback)); - parser->Start(); -} - -void WebBundleParser::ParseResponse(uint64_t response_offset, - uint64_t response_length, - ParseResponseCallback callback) { - ResponseParser* parser = new ResponseParser( - data_source_, response_offset, response_length, std::move(callback)); - parser->Start(); -} - -} // namespace data_decoder diff --git a/chromium/services/data_decoder/web_bundle_parser.h b/chromium/services/data_decoder/web_bundle_parser.h deleted file mode 100644 index 81fe780badc..00000000000 --- a/chromium/services/data_decoder/web_bundle_parser.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_H_ -#define SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_H_ - -#include <memory> - -#include "base/containers/flat_set.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/public/cpp/bindings/receiver.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h" - -namespace data_decoder { - -class WebBundleParser : public mojom::WebBundleParser { - public: - WebBundleParser(mojo::PendingReceiver<mojom::WebBundleParser> receiver, - mojo::PendingRemote<mojom::BundleDataSource> data_source); - ~WebBundleParser() override; - - private: - class MetadataParser; - class ResponseParser; - - class SharedBundleDataSource - : public base::RefCounted<SharedBundleDataSource>, - public mojom::BundleDataSource { - public: - class Observer; - - explicit SharedBundleDataSource( - mojo::PendingRemote<mojom::BundleDataSource> pending_data_source); - - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); - - // Implements mojom::BundleDataSource. - void Read(uint64_t offset, uint64_t length, ReadCallback callback) override; - - private: - friend class base::RefCounted<SharedBundleDataSource>; - - ~SharedBundleDataSource() override; - - void OnDisconnect(); - - mojo::Remote<mojom::BundleDataSource> data_source_; - base::flat_set<Observer*> observers_; - - DISALLOW_COPY_AND_ASSIGN(SharedBundleDataSource); - }; - - // mojom::WebBundleParser implementation. - void ParseMetadata(ParseMetadataCallback callback) override; - void ParseResponse(uint64_t response_offset, - uint64_t response_length, - ParseResponseCallback callback) override; - - mojo::Receiver<mojom::WebBundleParser> receiver_; - scoped_refptr<SharedBundleDataSource> data_source_; - - DISALLOW_COPY_AND_ASSIGN(WebBundleParser); -}; - -} // namespace data_decoder - -#endif // SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_H_ diff --git a/chromium/services/data_decoder/web_bundle_parser_factory.cc b/chromium/services/data_decoder/web_bundle_parser_factory.cc deleted file mode 100644 index 1ead65ea90c..00000000000 --- a/chromium/services/data_decoder/web_bundle_parser_factory.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/data_decoder/web_bundle_parser_factory.h" - -#include "base/bind_helpers.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "net/http/http_util.h" -#include "services/data_decoder/web_bundle_parser.h" - -namespace data_decoder { - -namespace { - -class FileDataSource final : public mojom::BundleDataSource { - public: - FileDataSource(mojo::PendingReceiver<mojom::BundleDataSource> receiver, - base::File file) - : receiver_(this, std::move(receiver)), file_(std::move(file)) { - receiver_.set_disconnect_handler(base::BindOnce( - &base::DeletePointer<FileDataSource>, base::Unretained(this))); - } - - private: - // Implements mojom::BundleDataSource. - void Read(uint64_t offset, uint64_t length, ReadCallback callback) override { - std::vector<uint8_t> buf(length); - int bytes = file_.Read(offset, reinterpret_cast<char*>(buf.data()), length); - if (bytes > 0) { - buf.resize(bytes); - std::move(callback).Run(std::move(buf)); - } else { - std::move(callback).Run(base::nullopt); - } - } - - mojo::Receiver<mojom::BundleDataSource> receiver_; - base::File file_; - - DISALLOW_COPY_AND_ASSIGN(FileDataSource); -}; - -} // namespace - -WebBundleParserFactory::WebBundleParserFactory() = default; - -WebBundleParserFactory::~WebBundleParserFactory() = default; - -std::unique_ptr<mojom::BundleDataSource> -WebBundleParserFactory::CreateFileDataSourceForTesting( - mojo::PendingReceiver<mojom::BundleDataSource> receiver, - base::File file) { - return std::make_unique<FileDataSource>(std::move(receiver), std::move(file)); -} - -void WebBundleParserFactory::GetParserForFile( - mojo::PendingReceiver<mojom::WebBundleParser> receiver, - base::File file) { - mojo::PendingRemote<mojom::BundleDataSource> remote_data_source; - auto data_source = std::make_unique<FileDataSource>( - remote_data_source.InitWithNewPipeAndPassReceiver(), std::move(file)); - GetParserForDataSource(std::move(receiver), std::move(remote_data_source)); - - // |data_source| will be destructed on |remote_data_source| destruction. - data_source.release(); -} - -void WebBundleParserFactory::GetParserForDataSource( - mojo::PendingReceiver<mojom::WebBundleParser> receiver, - mojo::PendingRemote<mojom::BundleDataSource> data_source) { - auto parser = std::make_unique<WebBundleParser>(std::move(receiver), - std::move(data_source)); - - // |parser| will be destructed on remote mojo ends' disconnection. - parser.release(); -} - -} // namespace data_decoder diff --git a/chromium/services/data_decoder/web_bundle_parser_factory.h b/chromium/services/data_decoder/web_bundle_parser_factory.h deleted file mode 100644 index 5430b5b92f0..00000000000 --- a/chromium/services/data_decoder/web_bundle_parser_factory.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_FACTORY_H_ -#define SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_FACTORY_H_ - -#include <memory> - -#include "base/files/file.h" -#include "base/macros.h" -#include "mojo/public/cpp/bindings/receiver.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "services/data_decoder/public/mojom/web_bundle_parser.mojom.h" - -namespace data_decoder { - -class WebBundleParserFactory : public mojom::WebBundleParserFactory { - public: - WebBundleParserFactory(); - ~WebBundleParserFactory() override; - - std::unique_ptr<mojom::BundleDataSource> CreateFileDataSourceForTesting( - mojo::PendingReceiver<mojom::BundleDataSource> receiver, - base::File file); - - private: - // mojom::WebBundleParserFactory implementation. - void GetParserForFile(mojo::PendingReceiver<mojom::WebBundleParser> receiver, - base::File file) override; - void GetParserForDataSource( - mojo::PendingReceiver<mojom::WebBundleParser> receiver, - mojo::PendingRemote<mojom::BundleDataSource> data_source) override; - - DISALLOW_COPY_AND_ASSIGN(WebBundleParserFactory); -}; - -} // namespace data_decoder - -#endif // SERVICES_DATA_DECODER_WEB_BUNDLE_PARSER_FACTORY_H_ diff --git a/chromium/services/data_decoder/web_bundle_parser_factory_unittest.cc b/chromium/services/data_decoder/web_bundle_parser_factory_unittest.cc deleted file mode 100644 index c6cecfa12f1..00000000000 --- a/chromium/services/data_decoder/web_bundle_parser_factory_unittest.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/data_decoder/web_bundle_parser_factory.h" - -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/optional.h" -#include "base/path_service.h" -#include "base/run_loop.h" -#include "base/test/bind_test_util.h" -#include "base/test/task_environment.h" -#include "mojo/public/cpp/bindings/pending_receiver.h" -#include "mojo/public/cpp/bindings/pending_remote.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace data_decoder { - -namespace { - -base::FilePath GetTestFilePath(const base::FilePath& path) { - base::FilePath test_path; - base::PathService::Get(base::DIR_SOURCE_ROOT, &test_path); - test_path = test_path.Append( - base::FilePath(FILE_PATH_LITERAL("services/test/data/web_bundle"))); - return test_path.Append(path); -} - -} // namespace - -class WebBundleParserFactoryTest : public testing::Test { - public: - WebBundleParserFactoryTest() - : factory_(std::make_unique<WebBundleParserFactory>()) {} - - std::unique_ptr<mojom::BundleDataSource> CreateFileDataSource( - mojo::PendingReceiver<mojom::BundleDataSource> receiver, - base::File file) { - return factory_->CreateFileDataSourceForTesting(std::move(receiver), - std::move(file)); - } - - void GetParserForFile(mojo::PendingReceiver<mojom::WebBundleParser> receiver, - base::File file) { - mojom::WebBundleParserFactory* factory = factory_.get(); - return factory->GetParserForFile(std::move(receiver), std::move(file)); - } - - private: - std::unique_ptr<WebBundleParserFactory> factory_; - base::test::TaskEnvironment task_environment_; -}; - -TEST_F(WebBundleParserFactoryTest, FileDataSource) { - base::FilePath test_file = - GetTestFilePath(base::FilePath(FILE_PATH_LITERAL("hello.wbn"))); - - base::File file(test_file, base::File::FLAG_OPEN | base::File::FLAG_READ); - ASSERT_TRUE(file.IsValid()); - int64_t file_length = file.GetLength(); - constexpr int64_t test_length = 16; - ASSERT_LE(test_length, file_length); - std::vector<uint8_t> first16b(test_length); - ASSERT_EQ(test_length, file.Read(0, reinterpret_cast<char*>(first16b.data()), - first16b.size())); - std::vector<uint8_t> last16b(test_length); - ASSERT_EQ(test_length, - file.Read(file_length - test_length, - reinterpret_cast<char*>(last16b.data()), last16b.size())); - - mojo::PendingRemote<mojom::BundleDataSource> remote; - auto data_source = CreateFileDataSource( - remote.InitWithNewPipeAndPassReceiver(), std::move(file)); - - base::Optional<std::vector<uint8_t>> result_data; - { - base::RunLoop run_loop; - data_source->Read( - /*offset=*/0, test_length, - base::BindLambdaForTesting( - [&result_data, - &run_loop](const base::Optional<std::vector<uint8_t>>& data) { - result_data = data; - run_loop.QuitClosure().Run(); - })); - run_loop.Run(); - } - ASSERT_TRUE(result_data); - EXPECT_EQ(first16b, *result_data); - - { - base::RunLoop run_loop; - data_source->Read( - file_length - test_length, test_length, - base::BindLambdaForTesting( - [&result_data, - &run_loop](const base::Optional<std::vector<uint8_t>>& data) { - result_data = data; - run_loop.QuitClosure().Run(); - })); - run_loop.Run(); - } - ASSERT_TRUE(result_data); - EXPECT_EQ(last16b, *result_data); - - { - base::RunLoop run_loop; - data_source->Read( - file_length - test_length, test_length + 1, - base::BindLambdaForTesting( - [&result_data, - &run_loop](const base::Optional<std::vector<uint8_t>>& data) { - result_data = data; - run_loop.QuitClosure().Run(); - })); - run_loop.Run(); - } - ASSERT_TRUE(result_data); - EXPECT_EQ(last16b, *result_data); - - { - base::RunLoop run_loop; - data_source->Read( - file_length + 1, test_length, - base::BindLambdaForTesting( - [&result_data, - &run_loop](const base::Optional<std::vector<uint8_t>>& data) { - result_data = data; - run_loop.QuitClosure().Run(); - })); - run_loop.Run(); - } - ASSERT_FALSE(result_data); -} - -TEST_F(WebBundleParserFactoryTest, GetParserForFile) { - base::File file( - GetTestFilePath(base::FilePath(FILE_PATH_LITERAL("hello.wbn"))), - base::File::FLAG_OPEN | base::File::FLAG_READ); - ASSERT_TRUE(file.IsValid()); - - mojo::Remote<mojom::WebBundleParser> parser; - GetParserForFile(parser.BindNewPipeAndPassReceiver(), std::move(file)); - - mojom::BundleMetadataPtr metadata; - { - base::RunLoop run_loop; - parser->ParseMetadata(base::BindLambdaForTesting( - [&metadata, &run_loop](mojom::BundleMetadataPtr parsed_metadata, - mojom::BundleMetadataParseErrorPtr error) { - metadata = std::move(parsed_metadata); - run_loop.QuitClosure().Run(); - })); - run_loop.Run(); - } - ASSERT_TRUE(metadata); - ASSERT_EQ(metadata->requests.size(), 4u); - - std::map<std::string, mojom::BundleResponsePtr> responses; - for (const auto& item : metadata->requests) { - ASSERT_TRUE(item.second->variants_value.empty()); - ASSERT_EQ(item.second->response_locations.size(), 1u); - base::RunLoop run_loop; - parser->ParseResponse(item.second->response_locations[0]->offset, - item.second->response_locations[0]->length, - base::BindLambdaForTesting( - [&item, &responses, &run_loop]( - mojom::BundleResponsePtr response, - mojom::BundleResponseParseErrorPtr error) { - ASSERT_TRUE(response); - ASSERT_FALSE(error); - responses[item.first.spec()] = - std::move(response); - run_loop.QuitClosure().Run(); - })); - run_loop.Run(); - } - ASSERT_TRUE(responses["https://test.example.org/"]); - EXPECT_EQ(responses["https://test.example.org/"]->response_code, 200); - EXPECT_EQ( - responses["https://test.example.org/"]->response_headers["content-type"], - "text/html; charset=utf-8"); - EXPECT_TRUE(responses["https://test.example.org/index.html"]); - EXPECT_TRUE(responses["https://test.example.org/manifest.webmanifest"]); - EXPECT_TRUE(responses["https://test.example.org/script.js"]); -} - -} // namespace data_decoder diff --git a/chromium/services/data_decoder/web_bundle_parser_fuzzer.cc b/chromium/services/data_decoder/web_bundle_parser_fuzzer.cc deleted file mode 100644 index df56a878f86..00000000000 --- a/chromium/services/data_decoder/web_bundle_parser_fuzzer.cc +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include <string> - -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/i18n/icu_util.h" -#include "base/run_loop.h" -#include "base/task/single_thread_task_executor.h" -#include "mojo/core/embedder/embedder.h" -#include "mojo/public/cpp/bindings/receiver_set.h" -#include "services/data_decoder/web_bundle_parser.h" -#include "services/data_decoder/web_bundle_parser_factory.h" - -namespace { - -class DataSource : public data_decoder::mojom::BundleDataSource { - public: - DataSource(const uint8_t* data, size_t size) : data_(data), size_(size) {} - - void Read(uint64_t offset, uint64_t length, ReadCallback callback) override { - if (offset >= size_) { - std::move(callback).Run(base::nullopt); - return; - } - const uint8_t* start = data_ + offset; - length = std::min(length, size_ - offset); - std::move(callback).Run(std::vector<uint8_t>(start, start + length)); - } - - void AddReceiver( - mojo::PendingReceiver<data_decoder::mojom::BundleDataSource> receiver) { - receivers_.Add(this, std::move(receiver)); - } - - private: - const uint8_t* data_; - size_t size_; - mojo::ReceiverSet<data_decoder::mojom::BundleDataSource> receivers_; -}; - -class WebBundleParserFuzzer { - public: - WebBundleParserFuzzer(const uint8_t* data, size_t size) - : data_source_(data, size) {} - - void FuzzBundle(base::RunLoop* run_loop) { - mojo::PendingRemote<data_decoder::mojom::BundleDataSource> - data_source_remote; - data_source_.AddReceiver( - data_source_remote.InitWithNewPipeAndPassReceiver()); - - data_decoder::WebBundleParserFactory factory_impl; - data_decoder::mojom::WebBundleParserFactory& factory = factory_impl; - factory.GetParserForDataSource(parser_.BindNewPipeAndPassReceiver(), - std::move(data_source_remote)); - - quit_loop_ = run_loop->QuitClosure(); - parser_->ParseMetadata(base::BindOnce( - &WebBundleParserFuzzer::OnParseMetadata, base::Unretained(this))); - } - - void OnParseMetadata(data_decoder::mojom::BundleMetadataPtr metadata, - data_decoder::mojom::BundleMetadataParseErrorPtr error) { - if (!metadata) { - std::move(quit_loop_).Run(); - return; - } - for (const auto& item : metadata->requests) { - for (auto& resp_location : item.second->response_locations) - locations_.push_back(std::move(resp_location)); - } - ParseResponses(0); - } - - void ParseResponses(size_t index) { - if (index >= locations_.size()) { - std::move(quit_loop_).Run(); - return; - } - - parser_->ParseResponse( - locations_[index]->offset, locations_[index]->length, - base::BindOnce(&WebBundleParserFuzzer::OnParseResponse, - base::Unretained(this), index)); - } - - void OnParseResponse(size_t index, - data_decoder::mojom::BundleResponsePtr response, - data_decoder::mojom::BundleResponseParseErrorPtr error) { - ParseResponses(index + 1); - } - - private: - mojo::Remote<data_decoder::mojom::WebBundleParser> parser_; - DataSource data_source_; - base::OnceClosure quit_loop_; - std::vector<data_decoder::mojom::BundleResponseLocationPtr> locations_; -}; - -struct Environment { - Environment() { - mojo::core::Init(); - CHECK(base::i18n::InitializeICU()); - } - - // Used by ICU integration. - base::AtExitManager at_exit_manager; - base::SingleThreadTaskExecutor task_executor; -}; - -} // namespace - -// Entry point for LibFuzzer. -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - static Environment* env = new Environment(); - - WebBundleParserFuzzer fuzzer(data, size); - base::RunLoop run_loop; - env->task_executor.task_runner()->PostTask( - FROM_HERE, base::BindOnce(&WebBundleParserFuzzer::FuzzBundle, - base::Unretained(&fuzzer), &run_loop)); - run_loop.Run(); - - return 0; -} diff --git a/chromium/services/data_decoder/web_bundle_parser_unittest.cc b/chromium/services/data_decoder/web_bundle_parser_unittest.cc deleted file mode 100644 index eb637bacf9e..00000000000 --- a/chromium/services/data_decoder/web_bundle_parser_unittest.cc +++ /dev/null @@ -1,700 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/data_decoder/web_bundle_parser.h" - -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/optional.h" -#include "base/path_service.h" -#include "base/test/bind_test_util.h" -#include "base/test/task_environment.h" -#include "components/cbor/writer.h" -#include "mojo/public/cpp/bindings/receiver_set.h" -#include "services/data_decoder/public/cpp/test_support/web_bundle_builder.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace data_decoder { - -namespace { - -constexpr char kFallbackUrl[] = "https://test.example.com/"; -constexpr char kManifestUrl[] = "https://test.example.com/manifest"; -constexpr char kValidityUrl[] = - "https://test.example.org/resource.validity.msg"; -const uint64_t kSignatureDate = 1564272000; // 2019-07-28T00:00:00Z -const uint64_t kSignatureDuration = 7 * 24 * 60 * 60; - -std::string GetTestFileContents(const base::FilePath& path) { - base::FilePath test_data_dir; - base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir); - test_data_dir = test_data_dir.Append( - base::FilePath(FILE_PATH_LITERAL("services/test/data/web_bundle"))); - - std::string contents; - EXPECT_TRUE(base::ReadFileToString(test_data_dir.Append(path), &contents)); - return contents; -} - -class TestDataSource : public mojom::BundleDataSource { - public: - explicit TestDataSource(const base::FilePath& path) - : data_(GetTestFileContents(path)) {} - explicit TestDataSource(const std::vector<uint8_t>& data) - : data_(reinterpret_cast<const char*>(data.data()), data.size()) {} - - void Read(uint64_t offset, uint64_t length, ReadCallback callback) override { - if (offset >= data_.size()) - std::move(callback).Run(base::nullopt); - const uint8_t* start = - reinterpret_cast<const uint8_t*>(data_.data()) + offset; - uint64_t available_length = std::min(length, data_.size() - offset); - std::move(callback).Run( - std::vector<uint8_t>(start, start + available_length)); - } - - base::StringPiece GetPayload(const mojom::BundleResponsePtr& response) { - return base::StringPiece(data_).substr(response->payload_offset, - response->payload_length); - } - - void AddReceiver(mojo::PendingReceiver<mojom::BundleDataSource> receiver) { - receivers_.Add(this, std::move(receiver)); - } - - private: - std::string data_; - mojo::ReceiverSet<mojom::BundleDataSource> receivers_; -}; - -using ParseBundleResult = - std::pair<mojom::BundleMetadataPtr, mojom::BundleMetadataParseErrorPtr>; - -ParseBundleResult ParseBundle(TestDataSource* data_source) { - mojo::PendingRemote<mojom::BundleDataSource> source_remote; - data_source->AddReceiver(source_remote.InitWithNewPipeAndPassReceiver()); - - mojo::PendingRemote<mojom::WebBundleParser> parser_remote; - WebBundleParser parser_impl(parser_remote.InitWithNewPipeAndPassReceiver(), - std::move(source_remote)); - data_decoder::mojom::WebBundleParser& parser = parser_impl; - - base::RunLoop run_loop; - ParseBundleResult result; - parser.ParseMetadata(base::BindLambdaForTesting( - [&result, &run_loop](mojom::BundleMetadataPtr metadata, - mojom::BundleMetadataParseErrorPtr error) { - result = std::make_pair(std::move(metadata), std::move(error)); - run_loop.QuitClosure().Run(); - })); - run_loop.Run(); - EXPECT_TRUE((result.first && !result.second) || - (!result.first && result.second)); - return result; -} - -void ExpectFormatErrorWithFallbackURL(ParseBundleResult result) { - ASSERT_TRUE(result.second); - EXPECT_EQ(result.second->type, mojom::BundleParseErrorType::kFormatError); - EXPECT_EQ(result.second->fallback_url, kFallbackUrl); -} - -// Finds the only response for |url|. The index entry for |url| must not have -// variants-value. -mojom::BundleResponseLocationPtr FindResponse( - const mojom::BundleMetadataPtr& metadata, - const GURL& url) { - const auto item = metadata->requests.find(url); - if (item == metadata->requests.end()) - return nullptr; - - const mojom::BundleIndexValuePtr& index_value = item->second; - EXPECT_TRUE(index_value->variants_value.empty()); - EXPECT_EQ(index_value->response_locations.size(), 1u); - if (index_value->response_locations.empty()) - return nullptr; - return index_value->response_locations[0].Clone(); -} - -mojom::BundleResponsePtr ParseResponse( - TestDataSource* data_source, - const mojom::BundleResponseLocationPtr& location) { - mojo::PendingRemote<mojom::BundleDataSource> source_remote; - data_source->AddReceiver(source_remote.InitWithNewPipeAndPassReceiver()); - - mojo::PendingRemote<mojom::WebBundleParser> parser_remote; - WebBundleParser parser_impl(parser_remote.InitWithNewPipeAndPassReceiver(), - std::move(source_remote)); - data_decoder::mojom::WebBundleParser& parser = parser_impl; - - base::RunLoop run_loop; - mojom::BundleResponsePtr result; - parser.ParseResponse( - location->offset, location->length, - base::BindLambdaForTesting( - [&result, &run_loop](mojom::BundleResponsePtr response, - mojom::BundleResponseParseErrorPtr error) { - result = std::move(response); - run_loop.QuitClosure().Run(); - })); - run_loop.Run(); - return result; -} - -cbor::Value CreateByteString(base::StringPiece s) { - return cbor::Value(base::as_bytes(base::make_span(s))); -} - -std::string AsString(const std::vector<uint8_t>& data) { - return std::string(reinterpret_cast<const char*>(data.data()), data.size()); -} - -} // namespace - -class WebBundleParserTest : public testing::Test { - private: - base::test::TaskEnvironment task_environment_; -}; - -TEST_F(WebBundleParserTest, WrongMagic) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - std::vector<uint8_t> bundle = builder.CreateBundle(); - bundle[3] ^= 1; - TestDataSource data_source(bundle); - - mojom::BundleMetadataParseErrorPtr error = ParseBundle(&data_source).second; - ASSERT_TRUE(error); - EXPECT_EQ(error->type, mojom::BundleParseErrorType::kFormatError); - EXPECT_TRUE(error->fallback_url.is_empty()); -} - -TEST_F(WebBundleParserTest, UnknownVersion) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - std::vector<uint8_t> bundle = builder.CreateBundle(); - // Modify the version string from "b1\0\0" to "q1\0\0". - ASSERT_EQ(bundle[11], 'b'); - bundle[11] = 'q'; - TestDataSource data_source(bundle); - - mojom::BundleMetadataParseErrorPtr error = ParseBundle(&data_source).second; - ASSERT_TRUE(error); - EXPECT_EQ(error->type, mojom::BundleParseErrorType::kVersionError); - EXPECT_EQ(error->fallback_url, kFallbackUrl); -} - -TEST_F(WebBundleParserTest, FallbackURLIsNotUTF8) { - test::WebBundleBuilder builder("https://test.example.com/\xcc", kManifestUrl); - std::vector<uint8_t> bundle = builder.CreateBundle(); - TestDataSource data_source(bundle); - - mojom::BundleMetadataParseErrorPtr error = ParseBundle(&data_source).second; - ASSERT_TRUE(error); - EXPECT_EQ(error->type, mojom::BundleParseErrorType::kFormatError); - EXPECT_TRUE(error->fallback_url.is_empty()); -} - -TEST_F(WebBundleParserTest, FallbackURLHasFragment) { - test::WebBundleBuilder builder("https://test.example.com/#fragment", - kManifestUrl); - std::vector<uint8_t> bundle = builder.CreateBundle(); - TestDataSource data_source(bundle); - - mojom::BundleMetadataParseErrorPtr error = ParseBundle(&data_source).second; - ASSERT_TRUE(error); - EXPECT_EQ(error->type, mojom::BundleParseErrorType::kFormatError); - EXPECT_TRUE(error->fallback_url.is_empty()); -} - -TEST_F(WebBundleParserTest, SectionLengthsTooLarge) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - std::string too_long_section_name(8192, 'x'); - builder.AddSection(too_long_section_name, cbor::Value(0)); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, DuplicateSectionName) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddSection("foo", cbor::Value(0)); - builder.AddSection("foo", cbor::Value(0)); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, SingleEntry) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - ASSERT_EQ(metadata->requests.size(), 1u); - auto location = FindResponse(metadata, GURL("https://test.example.com/")); - ASSERT_TRUE(location); - auto response = ParseResponse(&data_source, location); - ASSERT_TRUE(response); - EXPECT_EQ(response->response_code, 200); - EXPECT_EQ(response->response_headers.size(), 1u); - EXPECT_EQ(response->response_headers["content-type"], "text/plain"); - EXPECT_EQ(data_source.GetPayload(response), "payload"); -} - -TEST_F(WebBundleParserTest, InvalidRequestURL) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("", {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, RequestURLIsNotUTF8) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/\xcc", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, RequestURLHasBadScheme) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("file:///tmp/foo", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, RequestURLHasCredentials) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://user:passwd@test.example.com/", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, RequestURLHasFragment) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/#fragment", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, NoStatusInResponseHeaders) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{"content-type", "text/plain"}}, - "payload"); // ":status" is missing. - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - auto location = FindResponse(metadata, GURL("https://test.example.com/")); - ASSERT_TRUE(location); - ASSERT_FALSE(ParseResponse(&data_source, location)); -} - -TEST_F(WebBundleParserTest, InvalidResponseStatus) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{":status", "0200"}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - auto location = FindResponse(metadata, GURL("https://test.example.com/")); - ASSERT_TRUE(location); - ASSERT_FALSE(ParseResponse(&data_source, location)); -} - -TEST_F(WebBundleParserTest, ExtraPseudoInResponseHeaders) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange( - "https://test.example.com/", - {{":status", "200"}, {":foo", ""}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - auto location = FindResponse(metadata, GURL("https://test.example.com/")); - ASSERT_TRUE(location); - ASSERT_FALSE(ParseResponse(&data_source, location)); -} - -TEST_F(WebBundleParserTest, UpperCaseCharacterInHeaderName) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"Content-Type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - auto location = FindResponse(metadata, GURL("https://test.example.com/")); - ASSERT_TRUE(location); - ASSERT_FALSE(ParseResponse(&data_source, location)); -} - -TEST_F(WebBundleParserTest, InvalidHeaderValue) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"content-type", "\n"}}, "payload"); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - auto location = FindResponse(metadata, GURL("https://test.example.com/")); - ASSERT_TRUE(location); - ASSERT_FALSE(ParseResponse(&data_source, location)); -} - -TEST_F(WebBundleParserTest, NoContentTypeWithNonEmptyContent) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", {{":status", "200"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - auto location = FindResponse(metadata, GURL("https://test.example.com/")); - ASSERT_TRUE(location); - ASSERT_FALSE(ParseResponse(&data_source, location)); -} - -TEST_F(WebBundleParserTest, NoContentTypeWithEmptyContent) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", {{":status", "301"}}, ""); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - auto location = FindResponse(metadata, GURL("https://test.example.com/")); - ASSERT_TRUE(location); - ASSERT_TRUE(ParseResponse(&data_source, location)); -} - -TEST_F(WebBundleParserTest, Variants) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - auto location1 = builder.AddResponse( - {{":status", "200"}, {"content-type", "text/html"}}, "payload1"); - auto location2 = builder.AddResponse( - {{":status", "200"}, {"content-type", "text/plain"}}, "payload2"); - builder.AddIndexEntry("https://test.example.com/", - "Accept;text/html;text/plain", {location1, location2}); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - const auto& found = - metadata->requests.find(GURL("https://test.example.com/")); - ASSERT_NE(found, metadata->requests.end()); - const mojom::BundleIndexValuePtr& index_entry = found->second; - EXPECT_EQ(index_entry->variants_value, "Accept;text/html;text/plain"); - ASSERT_EQ(index_entry->response_locations.size(), 2u); - - auto response1 = - ParseResponse(&data_source, index_entry->response_locations[0]); - ASSERT_TRUE(response1); - EXPECT_EQ(data_source.GetPayload(response1), "payload1"); - auto response2 = - ParseResponse(&data_source, index_entry->response_locations[1]); - ASSERT_TRUE(response2); - EXPECT_EQ(data_source.GetPayload(response2), "payload2"); -} - -TEST_F(WebBundleParserTest, EmptyIndexEntry) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddIndexEntry("https://test.example.com/", "", {}); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, EmptyIndexEntryWithVariants) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddIndexEntry("https://test.example.com/", - "Accept;text/html;text/plain", {}); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, MultipleResponsesWithoutVariantsValue) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - auto location1 = builder.AddResponse( - {{":status", "200"}, {"content-type", "text/html"}}, "payload1"); - auto location2 = builder.AddResponse( - {{":status", "200"}, {"content-type", "text/plain"}}, "payload2"); - builder.AddIndexEntry("https://test.example.com/", "", - {location1, location2}); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, AllKnownSectionInCritical) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - cbor::Value::ArrayValue critical_section; - critical_section.emplace_back("manifest"); - critical_section.emplace_back("index"); - critical_section.emplace_back("critical"); - critical_section.emplace_back("responses"); - builder.AddSection("critical", cbor::Value(critical_section)); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); -} - -TEST_F(WebBundleParserTest, UnknownSectionInCritical) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - cbor::Value::ArrayValue critical_section; - critical_section.emplace_back("unknown_section_name"); - builder.AddSection("critical", cbor::Value(critical_section)); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, NoManifest) { - test::WebBundleBuilder builder(kFallbackUrl, std::string()); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); -} - -TEST_F(WebBundleParserTest, InvalidManifestURL) { - test::WebBundleBuilder builder(kFallbackUrl, "not-an-absolute-url"); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - TestDataSource data_source(builder.CreateBundle()); - - ExpectFormatErrorWithFallbackURL(ParseBundle(&data_source)); -} - -TEST_F(WebBundleParserTest, EmptySignaturesSection) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - // test::WebBundleBuilder omits signatures section if empty, so create it - // ourselves. - cbor::Value::ArrayValue signatures_section; - signatures_section.emplace_back(cbor::Value::ArrayValue()); // authorities - signatures_section.emplace_back( - cbor::Value::ArrayValue()); // vouched-subsets - builder.AddSection("signatures", cbor::Value(signatures_section)); - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - EXPECT_TRUE(metadata->authorities.empty()); - EXPECT_TRUE(metadata->vouched_subsets.empty()); -} - -TEST_F(WebBundleParserTest, SignaturesSection) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - - // Create a signatures section with some dummy data. - cbor::Value::MapValue authority; - authority.emplace("cert", CreateByteString("[cert]")); - authority.emplace("ocsp", CreateByteString("[ocsp]")); - authority.emplace("sct", CreateByteString("[sct]")); - builder.AddAuthority(std::move(authority)); - - cbor::Value signed_bytes = builder.CreateEncodedSigned( - kValidityUrl, "[auth-sha256]", kSignatureDate, - kSignatureDate + kSignatureDuration, "https://test.example.com/", - "[header-sha256]", "[payload-integrity]"); - cbor::Value::MapValue vouched_subset; - vouched_subset.emplace("authority", 0); - vouched_subset.emplace("sig", CreateByteString("[sig]")); - vouched_subset.emplace("signed", signed_bytes.Clone()); - builder.AddVouchedSubset(std::move(vouched_subset)); - - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - - ASSERT_EQ(metadata->authorities.size(), 1u); - EXPECT_EQ(AsString(metadata->authorities[0]->cert), "[cert]"); - ASSERT_TRUE(metadata->authorities[0]->ocsp.has_value()); - EXPECT_EQ(AsString(*metadata->authorities[0]->ocsp), "[ocsp]"); - ASSERT_TRUE(metadata->authorities[0]->sct.has_value()); - EXPECT_EQ(AsString(*metadata->authorities[0]->sct), "[sct]"); - - ASSERT_EQ(metadata->vouched_subsets.size(), 1u); - EXPECT_EQ(metadata->vouched_subsets[0]->authority, 0u); - EXPECT_EQ(AsString(metadata->vouched_subsets[0]->sig), "[sig]"); - EXPECT_EQ(AsString(metadata->vouched_subsets[0]->raw_signed), - signed_bytes.GetBytestringAsString()); - - const auto& parsed_signed = metadata->vouched_subsets[0]->parsed_signed; - EXPECT_EQ(parsed_signed->validity_url, kValidityUrl); - EXPECT_EQ(AsString(parsed_signed->auth_sha256), "[auth-sha256]"); - EXPECT_EQ(parsed_signed->date, kSignatureDate); - EXPECT_EQ(parsed_signed->expires, kSignatureDate + kSignatureDuration); - - EXPECT_EQ(parsed_signed->subset_hashes.size(), 1u); - const auto& hashes = - parsed_signed->subset_hashes[GURL("https://test.example.com/")]; - ASSERT_TRUE(hashes); - EXPECT_EQ(hashes->variants_value, ""); - ASSERT_EQ(hashes->resource_integrities.size(), 1u); - EXPECT_EQ(AsString(hashes->resource_integrities[0]->header_sha256), - "[header-sha256]"); - EXPECT_EQ(hashes->resource_integrities[0]->payload_integrity_header, - "[payload-integrity]"); -} - -TEST_F(WebBundleParserTest, MultipleSignatures) { - test::WebBundleBuilder builder(kFallbackUrl, kManifestUrl); - builder.AddExchange("https://test.example.com/", - {{":status", "200"}, {"content-type", "text/plain"}}, - "payload"); - - // Create a signatures section with some dummy data. - cbor::Value::MapValue authority1; - authority1.emplace("cert", CreateByteString("[cert1]")); - authority1.emplace("ocsp", CreateByteString("[ocsp]")); - authority1.emplace("sct", CreateByteString("[sct]")); - builder.AddAuthority(std::move(authority1)); - cbor::Value::MapValue authority2; - authority2.emplace("cert", CreateByteString("[cert2]")); - builder.AddAuthority(std::move(authority2)); - - cbor::Value signed_bytes1 = builder.CreateEncodedSigned( - kValidityUrl, "[auth-sha256]", kSignatureDate, - kSignatureDate + kSignatureDuration, "https://test.example.com/", - "[header-sha256]", "[payload-integrity]"); - cbor::Value::MapValue vouched_subset1; - vouched_subset1.emplace("authority", 0); - vouched_subset1.emplace("sig", CreateByteString("[sig1]")); - vouched_subset1.emplace("signed", signed_bytes1.Clone()); - builder.AddVouchedSubset(std::move(vouched_subset1)); - - cbor::Value signed_bytes2 = builder.CreateEncodedSigned( - kValidityUrl, "[auth-sha256-2]", kSignatureDate, - kSignatureDate + kSignatureDuration, "https://test.example.org/", - "[header-sha256-2]", "[payload-integrity-2]"); - cbor::Value::MapValue vouched_subset2; - vouched_subset2.emplace("authority", 1); - vouched_subset2.emplace("sig", CreateByteString("[sig2]")); - vouched_subset2.emplace("signed", signed_bytes2.Clone()); - builder.AddVouchedSubset(std::move(vouched_subset2)); - - TestDataSource data_source(builder.CreateBundle()); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - - ASSERT_EQ(metadata->authorities.size(), 2u); - EXPECT_EQ(AsString(metadata->authorities[0]->cert), "[cert1]"); - EXPECT_TRUE(metadata->authorities[0]->ocsp.has_value()); - EXPECT_TRUE(metadata->authorities[0]->sct.has_value()); - EXPECT_EQ(AsString(metadata->authorities[1]->cert), "[cert2]"); - EXPECT_FALSE(metadata->authorities[1]->ocsp.has_value()); - EXPECT_FALSE(metadata->authorities[1]->sct.has_value()); - - ASSERT_EQ(metadata->vouched_subsets.size(), 2u); - EXPECT_EQ(metadata->vouched_subsets[0]->authority, 0u); - EXPECT_EQ(AsString(metadata->vouched_subsets[0]->sig), "[sig1]"); - EXPECT_EQ(AsString(metadata->vouched_subsets[0]->raw_signed), - signed_bytes1.GetBytestringAsString()); - EXPECT_EQ(metadata->vouched_subsets[1]->authority, 1u); - EXPECT_EQ(AsString(metadata->vouched_subsets[1]->sig), "[sig2]"); - EXPECT_EQ(AsString(metadata->vouched_subsets[1]->raw_signed), - signed_bytes2.GetBytestringAsString()); -} - -TEST_F(WebBundleParserTest, ParseGoldenFile) { - TestDataSource data_source(base::FilePath(FILE_PATH_LITERAL("hello.wbn"))); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - ASSERT_EQ(metadata->requests.size(), 4u); - EXPECT_EQ(metadata->manifest_url, - "https://test.example.org/manifest.webmanifest"); - - std::map<std::string, mojom::BundleResponsePtr> responses; - for (const auto& item : metadata->requests) { - auto location = FindResponse(metadata, item.first); - ASSERT_TRUE(location); - auto resp = ParseResponse(&data_source, location); - ASSERT_TRUE(resp); - responses[item.first.spec()] = std::move(resp); - } - - ASSERT_TRUE(responses["https://test.example.org/"]); - EXPECT_EQ(responses["https://test.example.org/"]->response_code, 200); - EXPECT_EQ( - responses["https://test.example.org/"]->response_headers["content-type"], - "text/html; charset=utf-8"); - EXPECT_EQ(data_source.GetPayload(responses["https://test.example.org/"]), - GetTestFileContents( - base::FilePath(FILE_PATH_LITERAL("hello/index.html")))); - - EXPECT_TRUE(responses["https://test.example.org/index.html"]); - EXPECT_TRUE(responses["https://test.example.org/manifest.webmanifest"]); - EXPECT_TRUE(responses["https://test.example.org/script.js"]); -} - -TEST_F(WebBundleParserTest, ParseSignedFile) { - TestDataSource data_source( - base::FilePath(FILE_PATH_LITERAL("hello_signed.wbn"))); - - mojom::BundleMetadataPtr metadata = ParseBundle(&data_source).first; - ASSERT_TRUE(metadata); - EXPECT_EQ(metadata->authorities.size(), 1u); - ASSERT_EQ(metadata->vouched_subsets.size(), 1u); - EXPECT_EQ(metadata->vouched_subsets[0]->authority, 0u); - - const auto& parsed_signed = metadata->vouched_subsets[0]->parsed_signed; - EXPECT_EQ(parsed_signed->validity_url, kValidityUrl); - EXPECT_EQ(parsed_signed->date, kSignatureDate); - EXPECT_EQ(parsed_signed->expires, kSignatureDate + kSignatureDuration); - - EXPECT_EQ(parsed_signed->subset_hashes.size(), metadata->requests.size()); - const auto& hashes = - parsed_signed->subset_hashes[GURL("https://test.example.org/")]; - ASSERT_TRUE(hashes); - EXPECT_EQ(hashes->variants_value, ""); - ASSERT_EQ(hashes->resource_integrities.size(), 1u); - EXPECT_EQ(hashes->resource_integrities[0]->payload_integrity_header, - "digest/mi-sha256-03"); -} - -// TODO(crbug.com/969596): Add a test case that loads a wbn file with variants, -// once gen-bundle supports variants. - -} // namespace data_decoder diff --git a/chromium/services/device/BUILD.gn b/chromium/services/device/BUILD.gn index 05d4696dc52..62c53ee4ea5 100644 --- a/chromium/services/device/BUILD.gn +++ b/chromium/services/device/BUILD.gn @@ -8,7 +8,8 @@ if (is_android) { import("//build/config/android/rules.gni") } -is_serial_enabled_platform = is_win || (is_linux && use_udev) || is_mac +is_serial_enabled_platform = + is_win || ((is_linux || is_chromeos) && use_udev) || is_mac source_set("lib") { # This should be visible only to embedders of the Device Service, and the @@ -92,7 +93,7 @@ component("binder_overrides") { defines = [ "IS_DEVICE_SERVICE_BINDER_OVERRIDES_IMPL" ] } -is_linux_without_udev = is_linux && !use_udev +is_linux_without_udev = (is_linux || is_chromeos) && !use_udev source_set("perftests") { testonly = true @@ -247,6 +248,8 @@ source_set("tests") { if (is_serial_enabled_platform) { sources += [ + "serial/bluetooth_serial_port_impl_unittest.cc", + "serial/serial_device_enumerator_linux_unittest.cc", "serial/serial_device_enumerator_unittest.cc", "serial/serial_port_impl_unittest.cc", "serial/serial_port_manager_impl_unittest.cc", @@ -256,7 +259,14 @@ source_set("tests") { sources += [ "serial/serial_io_handler_posix_unittest.cc" ] } + if (is_linux || is_chromeos) { + deps += [ "//device/udev_linux:test_support" ] + } + deps += [ + "//device/bluetooth:mocks", + "//services/device/public/cpp/bluetooth:bluetooth", + "//services/device/public/cpp/serial:switches", "//services/device/serial", "//services/device/serial:test_support", ] diff --git a/chromium/services/device/README.md b/chromium/services/device/README.md new file mode 100644 index 00000000000..14c0f02ff68 --- /dev/null +++ b/chromium/services/device/README.md @@ -0,0 +1,2 @@ +Provides cross-platform public interfaces and per-platform backing +implementations of various features related to the user's physical device. diff --git a/chromium/services/device/battery/BUILD.gn b/chromium/services/device/battery/BUILD.gn index 6c379e3b183..ea2a638a79b 100644 --- a/chromium/services/device/battery/BUILD.gn +++ b/chromium/services/device/battery/BUILD.gn @@ -61,7 +61,7 @@ if (!is_android) { if (is_mac) { sources -= [ "battery_status_manager_default.cc" ] - libs = [ + frameworks = [ "CoreFoundation.framework", "IOKit.framework", ] diff --git a/chromium/services/device/battery/android/BUILD.gn b/chromium/services/device/battery/android/BUILD.gn index 70ecdb1d5da..8a2045a4c17 100644 --- a/chromium/services/device/battery/android/BUILD.gn +++ b/chromium/services/device/battery/android/BUILD.gn @@ -19,6 +19,7 @@ android_library("battery_monitor_java") { "//mojo/public/java:system_java", "//services/device/public/mojom:mojom_java", "//services/service_manager/public/java:service_manager_java", + "//third_party/android_deps:androidx_annotation_annotation_java", "//third_party/android_deps:com_google_code_findbugs_jsr305_java", ] } diff --git a/chromium/services/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java b/chromium/services/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java index 045f58aa007..44c2696c26f 100644 --- a/chromium/services/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java +++ b/chromium/services/device/battery/android/java/src/org/chromium/device/battery/BatteryStatusManager.java @@ -4,13 +4,11 @@ package org.chromium.device.battery; -import android.annotation.TargetApi; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; -import android.os.Build; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -39,13 +37,7 @@ class BatteryStatusManager { BatteryStatusManager.this.onReceive(intent); } }; - - // This is to workaround a Galaxy Nexus bug, see the comment in the constructor. - private final boolean mIgnoreBatteryPresentState; - - // Only used in L (API level 21) and higher. private AndroidBatteryManagerWrapper mAndroidBatteryManager; - private boolean mEnabled; @VisibleForTesting @@ -56,37 +48,31 @@ class BatteryStatusManager { mBatteryManager = batteryManager; } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) public int getIntProperty(int id) { return mBatteryManager.getIntProperty(id); } } - private BatteryStatusManager(BatteryStatusCallback callback, boolean ignoreBatteryPresentState, - @Nullable AndroidBatteryManagerWrapper batteryManager) { + private BatteryStatusManager( + BatteryStatusCallback callback, @Nullable AndroidBatteryManagerWrapper batteryManager) { mCallback = callback; - mIgnoreBatteryPresentState = ignoreBatteryPresentState; mAndroidBatteryManager = batteryManager; } BatteryStatusManager(BatteryStatusCallback callback) { - // BatteryManager.EXTRA_PRESENT appears to be unreliable on Galaxy Nexus, - // Android 4.2.1, it always reports false. See http://crbug.com/384348. - this(callback, Build.MODEL.equals("Galaxy Nexus"), - Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP - ? new AndroidBatteryManagerWrapper( - (BatteryManager) ContextUtils.getApplicationContext() - .getSystemService(Context.BATTERY_SERVICE)) - : null); + this(callback, + new AndroidBatteryManagerWrapper( + (BatteryManager) ContextUtils.getApplicationContext().getSystemService( + Context.BATTERY_SERVICE))); } /** * Creates a BatteryStatusManager without the Galaxy Nexus workaround for consistency in * testing. */ - static BatteryStatusManager createBatteryStatusManagerForTesting(Context context, + static BatteryStatusManager createBatteryStatusManagerForTesting( BatteryStatusCallback callback, @Nullable AndroidBatteryManagerWrapper batteryManager) { - return new BatteryStatusManager(callback, false, batteryManager); + return new BatteryStatusManager(callback, batteryManager); } /** @@ -120,9 +106,7 @@ class BatteryStatusManager { return; } - boolean present = mIgnoreBatteryPresentState - ? true - : intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false); + boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false); int pluggedStatus = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); if (!present || pluggedStatus == -1) { @@ -156,16 +140,13 @@ class BatteryStatusManager { batteryStatus.level = level; if (mAndroidBatteryManager != null) { - updateBatteryStatusForLollipop(batteryStatus); + updateBatteryStatus(batteryStatus); } mCallback.onBatteryStatusChanged(batteryStatus); } - private void updateBatteryStatusForLollipop(BatteryStatus batteryStatus) { - assert mAndroidBatteryManager != null; - - // On Lollipop we can provide a better estimate for chargingTime and dischargingTime. + private void updateBatteryStatus(BatteryStatus batteryStatus) { double remainingCapacityRatio = mAndroidBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) / 100.0; diff --git a/chromium/services/device/battery/android/javatests/src/org/chromium/device/battery/BatteryStatusManagerTest.java b/chromium/services/device/battery/android/javatests/src/org/chromium/device/battery/BatteryStatusManagerTest.java index b489fd374db..7befccfbb8f 100644 --- a/chromium/services/device/battery/android/javatests/src/org/chromium/device/battery/BatteryStatusManagerTest.java +++ b/chromium/services/device/battery/android/javatests/src/org/chromium/device/battery/BatteryStatusManagerTest.java @@ -6,8 +6,6 @@ package org.chromium.device.battery; import android.content.Intent; import android.os.BatteryManager; -import android.os.Build; -import android.support.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -99,7 +97,7 @@ public class BatteryStatusManagerTest { public void initializeBatteryManager(FakeAndroidBatteryManager managerForTesting) { mManager = BatteryStatusManager.createBatteryStatusManagerForTesting( - InstrumentationRegistry.getContext(), mCallback, managerForTesting); + mCallback, managerForTesting); } @Test @@ -188,9 +186,7 @@ public class BatteryStatusManagerTest { @Test @SmallTest - public void testLollipopChargingTimeEstimate() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; - + public void testChargingTimeEstimate() { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.putExtra(BatteryManager.EXTRA_PRESENT, true); intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB); @@ -209,9 +205,7 @@ public class BatteryStatusManagerTest { @Test @SmallTest - public void testLollipopDischargingTimeEstimate() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; - + public void testDischargingTimeEstimate() { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.putExtra(BatteryManager.EXTRA_PRESENT, true); intent.putExtra(BatteryManager.EXTRA_PLUGGED, 0); @@ -231,9 +225,7 @@ public class BatteryStatusManagerTest { @Test @SmallTest - public void testLollipopDischargingTimeEstimateRounding() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; - + public void testDischargingTimeEstimateRounding() { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.putExtra(BatteryManager.EXTRA_PRESENT, true); intent.putExtra(BatteryManager.EXTRA_PLUGGED, 0); diff --git a/chromium/services/device/battery/battery_status_manager_win.cc b/chromium/services/device/battery/battery_status_manager_win.cc index 3e8ba408e51..2a27abb55b9 100644 --- a/chromium/services/device/battery/battery_status_manager_win.cc +++ b/chromium/services/device/battery/battery_status_manager_win.cc @@ -133,7 +133,7 @@ class BatteryStatusObserver { if (!window_->CreateNamed( base::BindRepeating(&BatteryStatusObserver::HandleMessage, base::Unretained(this)), - base::string16(kWindowClassName))) { + kWindowClassName)) { LOG(ERROR) << "Failed to create message window: " << kWindowClassName; window_.reset(); return false; diff --git a/chromium/services/device/bluetooth/bluetooth_system.cc b/chromium/services/device/bluetooth/bluetooth_system.cc index b63cd072365..7925dac8109 100644 --- a/chromium/services/device/bluetooth/bluetooth_system.cc +++ b/chromium/services/device/bluetooth/bluetooth_system.cc @@ -19,6 +19,7 @@ #include "device/bluetooth/dbus/bluetooth_adapter_client.h" #include "device/bluetooth/dbus/bluetooth_device_client.h" #include "device/bluetooth/dbus/bluez_dbus_manager.h" +#include "device/bluetooth/public/cpp/bluetooth_address.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" namespace device { @@ -209,8 +210,7 @@ void BluetoothSystem::GetAvailableDevices( for (const auto& device_path : device_paths) { auto* properties = GetBluetoothDeviceClient()->GetProperties(device_path); std::array<uint8_t, 6> parsed_address; - if (!BluetoothDevice::ParseAddress(properties->address.value(), - parsed_address)) { + if (!ParseBluetoothAddress(properties->address.value(), parsed_address)) { LOG(WARNING) << "Failed to parse device address '" << properties->address.value() << "' for " << device_path.value(); diff --git a/chromium/services/device/device_service.cc b/chromium/services/device/device_service.cc index 7fe052a5553..bb284732ba8 100644 --- a/chromium/services/device/device_service.cc +++ b/chromium/services/device/device_service.cc @@ -43,7 +43,7 @@ #include "services/device/vibration/vibration_manager_impl.h" #endif -#if defined(OS_LINUX) && defined(USE_UDEV) +#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV) #include "services/device/hid/input_service_linux.h" #endif @@ -125,11 +125,11 @@ DeviceService::DeviceService( geolocation_api_key_(geolocation_api_key), wake_lock_provider_(file_task_runner_, wake_lock_context_callback_) { receivers_.Add(this, std::move(receiver)); -#if (defined(OS_LINUX) && defined(USE_UDEV)) || defined(OS_WIN) || \ - defined(OS_MACOSX) +#if ((defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV)) || \ + defined(OS_WIN) || defined(OS_MAC) serial_port_manager_ = std::make_unique<SerialPortManagerImpl>( io_task_runner_, base::ThreadTaskRunnerHandle::Get()); -#if defined(OS_MACOSX) +#if defined(OS_MAC) // On macOS the SerialDeviceEnumerator needs to run on the UI thread so that // it has access to a CFRunLoop where it can register a notification source. serial_port_manager_task_runner_ = base::ThreadTaskRunnerHandle::Get(); @@ -155,8 +155,8 @@ DeviceService::~DeviceService() { // it's not really important that this runs anyway. device::BatteryStatusService::GetInstance()->Shutdown(); #endif -#if (defined(OS_LINUX) && defined(USE_UDEV)) || defined(OS_WIN) || \ - defined(OS_MACOSX) +#if ((defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV)) || \ + defined(OS_WIN) || defined(OS_MAC) serial_port_manager_task_runner_->DeleteSoon(FROM_HERE, std::move(serial_port_manager_)); #endif @@ -230,7 +230,7 @@ void DeviceService::BindMtpManager( } #endif -#if defined(OS_LINUX) && defined(USE_UDEV) +#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV) void DeviceService::BindInputDeviceManager( mojo::PendingReceiver<mojom::InputDeviceManager> receiver) { file_task_runner_->PostTask( @@ -311,8 +311,8 @@ void DeviceService::BindSensorProvider( void DeviceService::BindSerialPortManager( mojo::PendingReceiver<mojom::SerialPortManager> receiver) { -#if (defined(OS_LINUX) && defined(USE_UDEV)) || defined(OS_WIN) || \ - defined(OS_MACOSX) +#if ((defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV)) || \ + defined(OS_WIN) || defined(OS_MAC) DCHECK(serial_port_manager_task_runner_); serial_port_manager_task_runner_->PostTask( FROM_HERE, base::BindOnce(&SerialPortManagerImpl::Bind, diff --git a/chromium/services/device/device_service.h b/chromium/services/device/device_service.h index 43744315bf1..c5e950b095c 100644 --- a/chromium/services/device/device_service.h +++ b/chromium/services/device/device_service.h @@ -50,7 +50,7 @@ #include "services/device/public/mojom/bluetooth_system.mojom.h" #endif -#if defined(OS_LINUX) && defined(USE_UDEV) +#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV) #include "services/device/public/mojom/input_service.mojom.h" #endif @@ -148,7 +148,7 @@ class DeviceService : public mojom::DeviceService { void BindGeolocationControl( mojo::PendingReceiver<mojom::GeolocationControl> receiver) override; -#if defined(OS_LINUX) && defined(USE_UDEV) +#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV) void BindInputDeviceManager( mojo::PendingReceiver<mojom::InputDeviceManager> receiver) override; #endif @@ -234,8 +234,8 @@ class DeviceService : public mojom::DeviceService { std::unique_ptr<HidManagerImpl> hid_manager_; #endif -#if (defined(OS_LINUX) && defined(USE_UDEV)) || defined(OS_WIN) || \ - defined(OS_MACOSX) +#if ((defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV)) || \ + defined(OS_WIN) || defined(OS_MAC) // Requests for the SerialPortManager interface must be bound to // |serial_port_manager_| on |serial_port_manager_task_runner_| and it will // be destroyed on that sequence. diff --git a/chromium/services/device/generic_sensor/BUILD.gn b/chromium/services/device/generic_sensor/BUILD.gn index b491d557fa2..6f969c000ae 100644 --- a/chromium/services/device/generic_sensor/BUILD.gn +++ b/chromium/services/device/generic_sensor/BUILD.gn @@ -84,7 +84,7 @@ source_set("generic_sensor") { if (is_mac) { deps += [ "//third_party/sudden_motion_sensor" ] - libs = [ + frameworks = [ "CoreFoundation.framework", "IOKit.framework", ] diff --git a/chromium/services/device/generic_sensor/android/java/src/org/chromium/device/sensors/PlatformSensor.java b/chromium/services/device/generic_sensor/android/java/src/org/chromium/device/sensors/PlatformSensor.java index d16d43dd26c..43df6514a0e 100644 --- a/chromium/services/device/generic_sensor/android/java/src/org/chromium/device/sensors/PlatformSensor.java +++ b/chromium/services/device/generic_sensor/android/java/src/org/chromium/device/sensors/PlatformSensor.java @@ -7,7 +7,6 @@ package org.chromium.device.sensors; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; -import android.os.Build; import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; @@ -109,12 +108,9 @@ public class PlatformSensor implements SensorEventListener { */ @CalledByNative protected int getReportingMode() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return mSensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS - ? ReportingMode.CONTINUOUS - : ReportingMode.ON_CHANGE; - } - return ReportingMode.CONTINUOUS; + return mSensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS + ? ReportingMode.CONTINUOUS + : ReportingMode.ON_CHANGE; } /** diff --git a/chromium/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc b/chromium/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc index b43c55c56b8..7d33e74f4e0 100644 --- a/chromium/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc +++ b/chromium/services/device/generic_sensor/platform_sensor_and_provider_unittest_linux.cc @@ -313,7 +313,7 @@ class PlatformSensorAndProviderLinuxTest : public ::testing::Test { void GenerateDeviceRemovedEvent(const base::FilePath& sensor_dir) { { base::ScopedAllowBlockingForTesting allow_blocking; - EXPECT_TRUE(base::DeleteFileRecursively(sensor_dir)); + EXPECT_TRUE(base::DeletePathRecursively(sensor_dir)); } bool success = provider_->blocking_task_runner_->PostTask( FROM_HERE, diff --git a/chromium/services/device/generic_sensor/platform_sensor_provider.cc b/chromium/services/device/generic_sensor/platform_sensor_provider.cc index f1f7159611e..6922319bf9d 100644 --- a/chromium/services/device/generic_sensor/platform_sensor_provider.cc +++ b/chromium/services/device/generic_sensor/platform_sensor_provider.cc @@ -4,7 +4,7 @@ #include "services/device/generic_sensor/platform_sensor_provider.h" -#if defined(OS_MACOSX) +#if defined(OS_MAC) #include "services/device/generic_sensor/platform_sensor_provider_mac.h" #elif defined(OS_ANDROID) #include "services/device/generic_sensor/platform_sensor_provider_android.h" @@ -23,7 +23,7 @@ namespace device { // static std::unique_ptr<PlatformSensorProvider> PlatformSensorProvider::Create() { -#if defined(OS_MACOSX) +#if defined(OS_MAC) return std::make_unique<PlatformSensorProviderMac>(); #elif defined(OS_ANDROID) return std::make_unique<PlatformSensorProviderAndroid>(); diff --git a/chromium/services/device/generic_sensor/platform_sensor_reader_win.cc b/chromium/services/device/generic_sensor/platform_sensor_reader_win.cc index 4111af1121a..63bd89b7b2d 100644 --- a/chromium/services/device/generic_sensor/platform_sensor_reader_win.cc +++ b/chromium/services/device/generic_sensor/platform_sensor_reader_win.cc @@ -389,15 +389,15 @@ Microsoft::WRL::ComPtr<ISensor> PlatformSensorReaderWin32::GetSensorForType( Microsoft::WRL::ComPtr<ISensorManager> sensor_manager) { Microsoft::WRL::ComPtr<ISensor> sensor; Microsoft::WRL::ComPtr<ISensorCollection> sensor_collection; - HRESULT hr = sensor_manager->GetSensorsByType( - sensor_type, sensor_collection.GetAddressOf()); + HRESULT hr = + sensor_manager->GetSensorsByType(sensor_type, &sensor_collection); if (FAILED(hr) || !sensor_collection) return sensor; ULONG count = 0; hr = sensor_collection->GetCount(&count); if (SUCCEEDED(hr) && count > 0) - sensor_collection->GetAt(0, sensor.GetAddressOf()); + sensor_collection->GetAt(0, &sensor); return sensor; } @@ -484,7 +484,7 @@ bool PlatformSensorReaderWin32::SetReportingInterval( return false; Microsoft::WRL::ComPtr<IPortableDeviceValues> return_props; - hr = sensor_->SetProperties(props.Get(), return_props.GetAddressOf()); + hr = sensor_->SetProperties(props.Get(), &return_props); return SUCCEEDED(hr); } diff --git a/chromium/services/device/geolocation/BUILD.gn b/chromium/services/device/geolocation/BUILD.gn index bc366b81794..8b1beef4892 100644 --- a/chromium/services/device/geolocation/BUILD.gn +++ b/chromium/services/device/geolocation/BUILD.gn @@ -98,7 +98,7 @@ source_set("geolocation") { if (is_chromeos || (is_linux && !use_dbus)) { sources -= [ "wifi_data_provider_linux.cc" ] } - if (is_linux && use_dbus) { + if ((is_linux || is_chromeos) && use_dbus) { sources -= [ "empty_wifi_data_provider.cc" ] deps += [ "//dbus" ] } @@ -116,7 +116,7 @@ source_set("geolocation") { } if (is_mac) { - libs = [ + frameworks = [ "CoreWLAN.framework", "Foundation.framework", "CoreLocation.framework", @@ -164,6 +164,7 @@ if (is_android) { "//base:jni_java", "//components/location/android:location_java", "//services/device/public/java:geolocation_java", + "//third_party/android_deps:androidx_annotation_annotation_java", ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] } diff --git a/chromium/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java b/chromium/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java index c3651e74a6b..ebdfcc7ab4a 100644 --- a/chromium/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java +++ b/chromium/services/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAdapter.java @@ -6,8 +6,6 @@ package org.chromium.device.geolocation; import android.location.Location; -import androidx.annotation.VisibleForTesting; - import org.chromium.base.Log; import org.chromium.base.ThreadUtils; import org.chromium.base.annotations.CalledByNative; @@ -23,7 +21,6 @@ import java.util.concurrent.FutureTask; * content/browser/geolocation/location_api_adapter_android.h. * Based on android.webkit.GeolocationService.java */ -@VisibleForTesting public class LocationProviderAdapter { private static final String TAG = "LocationProvider"; diff --git a/chromium/services/device/geolocation/geolocation_provider_impl.cc b/chromium/services/device/geolocation/geolocation_provider_impl.cc index 26edf4f9494..15dad9765c6 100644 --- a/chromium/services/device/geolocation/geolocation_provider_impl.cc +++ b/chromium/services/device/geolocation/geolocation_provider_impl.cc @@ -172,7 +172,7 @@ void GeolocationProviderImpl::OnClientsChanged() { } else { if (!IsRunning()) { base::Thread::Options options; -#if defined(OS_MACOSX) +#if defined(OS_MAC) options.message_pump_type = base::MessagePumpType::NS_RUNLOOP; #endif StartWithOptions(options); diff --git a/chromium/services/device/geolocation/location_arbitrator.cc b/chromium/services/device/geolocation/location_arbitrator.cc index 394ec28bc4b..e5a1f312fdb 100644 --- a/chromium/services/device/geolocation/location_arbitrator.cc +++ b/chromium/services/device/geolocation/location_arbitrator.cc @@ -156,7 +156,7 @@ LocationArbitrator::NewNetworkLocationProvider( std::unique_ptr<LocationProvider> LocationArbitrator::NewSystemLocationProvider() { -#if defined(OS_LINUX) || defined(OS_FUCHSIA) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA) return nullptr; #else return device::NewSystemLocationProvider(); diff --git a/chromium/services/device/geolocation/network_location_provider_unittest.cc b/chromium/services/device/geolocation/network_location_provider_unittest.cc index 62b5157038f..59af9998dc7 100644 --- a/chromium/services/device/geolocation/network_location_provider_unittest.cc +++ b/chromium/services/device/geolocation/network_location_provider_unittest.cc @@ -319,7 +319,7 @@ TEST_F(GeolocationNetworkProviderTest, NonEmptyApiKey) { const GURL& request_url = test_url_loader_factory_.pending_requests()->back().request.url; EXPECT_TRUE(request_url.has_query()); - EXPECT_TRUE(request_url.query_piece().starts_with("key=")); + EXPECT_TRUE(base::StartsWith(request_url.query_piece(), "key=")); } // Tests that, after StartProvider(), a TestURLFetcher can be extracted, diff --git a/chromium/services/device/hid/BUILD.gn b/chromium/services/device/hid/BUILD.gn index 494ecc11848..dbe29872689 100644 --- a/chromium/services/device/hid/BUILD.gn +++ b/chromium/services/device/hid/BUILD.gn @@ -41,7 +41,7 @@ source_set("hid") { "//services/device/public/mojom", ] - if (is_linux && use_udev) { + if ((is_linux || is_chromeos) && use_udev) { sources += [ "hid_service_linux.cc", "hid_service_linux.h", diff --git a/chromium/services/device/hid/hid_connection_impl_unittest.cc b/chromium/services/device/hid/hid_connection_impl_unittest.cc index cde9f8e444c..1a1b4613e00 100644 --- a/chromium/services/device/hid/hid_connection_impl_unittest.cc +++ b/chromium/services/device/hid/hid_connection_impl_unittest.cc @@ -18,7 +18,7 @@ namespace device { namespace { -#if defined(OS_MACOSX) +#if defined(OS_MAC) const uint64_t kTestDeviceId = 123; #elif defined(OS_WIN) const wchar_t* kTestDeviceId = L"123"; diff --git a/chromium/services/device/hid/hid_device_info.h b/chromium/services/device/hid/hid_device_info.h index eca93d9aa21..0c84e730f09 100644 --- a/chromium/services/device/hid/hid_device_info.h +++ b/chromium/services/device/hid/hid_device_info.h @@ -19,10 +19,10 @@ namespace device { -#if defined(OS_MACOSX) +#if defined(OS_MAC) typedef uint64_t HidPlatformDeviceId; #elif defined(OS_WIN) -typedef base::string16 HidPlatformDeviceId; +typedef std::wstring HidPlatformDeviceId; #else typedef std::string HidPlatformDeviceId; #endif diff --git a/chromium/services/device/hid/hid_manager_unittest.cc b/chromium/services/device/hid/hid_manager_unittest.cc index 7334f83a72c..6a5066d6b13 100644 --- a/chromium/services/device/hid/hid_manager_unittest.cc +++ b/chromium/services/device/hid/hid_manager_unittest.cc @@ -22,7 +22,7 @@ namespace device { namespace { -#if defined(OS_MACOSX) +#if defined(OS_MAC) const uint64_t kTestDeviceIds[] = {0, 1, 2}; #elif defined(OS_WIN) const wchar_t* const kTestDeviceIds[] = {L"0", L"1", L"2"}; diff --git a/chromium/services/device/hid/hid_service.cc b/chromium/services/device/hid/hid_service.cc index f36737283e3..056c83b638d 100644 --- a/chromium/services/device/hid/hid_service.cc +++ b/chromium/services/device/hid/hid_service.cc @@ -14,9 +14,9 @@ #include "build/build_config.h" #include "components/device_event_log/device_event_log.h" -#if defined(OS_LINUX) && defined(USE_UDEV) +#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV) #include "services/device/hid/hid_service_linux.h" -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) #include "services/device/hid/hid_service_mac.h" #elif defined(OS_WIN) #include "services/device/hid/hid_service_win.h" @@ -34,9 +34,9 @@ constexpr base::TaskTraits HidService::kBlockingTaskTraits; // static std::unique_ptr<HidService> HidService::Create() { -#if defined(OS_LINUX) && defined(USE_UDEV) +#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(USE_UDEV) return base::WrapUnique(new HidServiceLinux()); -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) return base::WrapUnique(new HidServiceMac()); #elif defined(OS_WIN) return base::WrapUnique(new HidServiceWin()); diff --git a/chromium/services/device/hid/hid_service_win.cc b/chromium/services/device/hid/hid_service_win.cc index 80fff2d39a8..e9233de42b4 100644 --- a/chromium/services/device/hid/hid_service_win.cc +++ b/chromium/services/device/hid/hid_service_win.cc @@ -62,7 +62,7 @@ bool GetDeviceInfoAndPathFromInterface( HDEVINFO device_info_set, SP_DEVICE_INTERFACE_DATA& device_interface_data, SP_DEVINFO_DATA* device_info_data, - base::string16* device_path) { + std::wstring* device_path) { // Get the required buffer size. When called with // DeviceInterfaceDetailData == nullptr and DeviceInterfaceDetailSize == 0, // SetupDiGetDeviceInterfaceDetail returns the required buffer size at @@ -93,8 +93,7 @@ bool GetDeviceInfoAndPathFromInterface( // Windows uses case-insensitive paths and may return paths that differ only // by case. Canonicalize the device path by converting to lowercase. - base::string16 path = - base::string16(device_interface_detail_data->DevicePath); + std::wstring path = device_interface_detail_data->DevicePath; DCHECK(base::IsStringASCII(path)); *device_path = base::ToLowerASCII(path); return true; @@ -104,7 +103,7 @@ bool GetDeviceInfoAndPathFromInterface( // |device_path|, or an invalid ScopedDevInfo if there was an error while // creating the device set. The device info is returned in |device_info_data|. base::win::ScopedDevInfo GetDeviceInfoFromPath( - const base::string16& device_path, + const std::wstring& device_path, SP_DEVINFO_DATA* device_info_data) { base::win::ScopedDevInfo device_info_set(SetupDiGetClassDevs( &GUID_DEVINTERFACE_HID, /*Enumerator=*/nullptr, @@ -119,7 +118,7 @@ base::win::ScopedDevInfo GetDeviceInfoFromPath( return base::win::ScopedDevInfo(); } - base::string16 intf_device_path; + std::wstring intf_device_path; GetDeviceInfoAndPathFromInterface(device_info_set.get(), device_interface_data, device_info_data, &intf_device_path); @@ -193,7 +192,7 @@ void HidServiceWin::EnumerateBlocking( ++device_index) { SP_DEVINFO_DATA dev_info_data = {0}; dev_info_data.cbSize = sizeof(dev_info_data); - base::string16 device_path; + std::wstring device_path; if (!GetDeviceInfoAndPathFromInterface(dev_info.get(), device_interface_data, &dev_info_data, &device_path)) { @@ -207,7 +206,7 @@ void HidServiceWin::EnumerateBlocking( continue; } std::string physical_device_id = - base::UTF16ToUTF8(base::win::String16FromGUID(container_id)); + base::WideToUTF8(base::win::WStringFromGUID(container_id)); AddDeviceBlocking(service, task_runner, device_path, physical_device_id); } @@ -264,7 +263,7 @@ void HidServiceWin::CollectInfoFromValueCaps( void HidServiceWin::AddDeviceBlocking( base::WeakPtr<HidServiceWin> service, scoped_refptr<base::SequencedTaskRunner> task_runner, - const base::string16& device_path, + const std::wstring& device_path, const std::string& physical_device_id) { base::win::ScopedHandle device_handle(OpenDevice(device_path)); if (!device_handle.IsValid()) { @@ -361,7 +360,7 @@ void HidServiceWin::AddDeviceBlocking( } void HidServiceWin::OnDeviceAdded(const GUID& class_guid, - const base::string16& device_path) { + const std::wstring& device_path) { SP_DEVINFO_DATA device_info_data = {0}; device_info_data.cbSize = sizeof(device_info_data); auto device_info_set = GetDeviceInfoFromPath(device_path, &device_info_data); @@ -374,7 +373,7 @@ void HidServiceWin::OnDeviceAdded(const GUID& class_guid, return; } std::string physical_device_id = - base::UTF16ToUTF8(base::win::String16FromGUID(container_id)); + base::WideToUTF8(base::win::WStringFromGUID(container_id)); blocking_task_runner_->PostTask( FROM_HERE, base::BindOnce(&HidServiceWin::AddDeviceBlocking, @@ -383,7 +382,7 @@ void HidServiceWin::OnDeviceAdded(const GUID& class_guid, } void HidServiceWin::OnDeviceRemoved(const GUID& class_guid, - const base::string16& device_path) { + const std::wstring& device_path) { // Execute a no-op closure on the file task runner to synchronize with any // devices that are still being enumerated. blocking_task_runner_->PostTaskAndReply( @@ -394,7 +393,7 @@ void HidServiceWin::OnDeviceRemoved(const GUID& class_guid, // static base::win::ScopedHandle HidServiceWin::OpenDevice( - const base::string16& device_path) { + const std::wstring& device_path) { base::win::ScopedHandle file( CreateFile(device_path.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, diff --git a/chromium/services/device/hid/hid_service_win.h b/chromium/services/device/hid/hid_service_win.h index ac6745f3aaf..54779511339 100644 --- a/chromium/services/device/hid/hid_service_win.h +++ b/chromium/services/device/hid/hid_service_win.h @@ -59,17 +59,17 @@ class HidServiceWin : public HidService, public DeviceMonitorWin::Observer { static void AddDeviceBlocking( base::WeakPtr<HidServiceWin> service, scoped_refptr<base::SequencedTaskRunner> task_runner, - const base::string16& device_path, + const std::wstring& device_path, const std::string& physical_device_id); // DeviceMonitorWin::Observer implementation: void OnDeviceAdded(const GUID& class_guid, - const base::string16& device_path) override; + const std::wstring& device_path) override; void OnDeviceRemoved(const GUID& class_guid, - const base::string16& device_path) override; + const std::wstring& device_path) override; // Tries to open the device read-write and falls back to read-only. - static base::win::ScopedHandle OpenDevice(const base::string16& device_path); + static base::win::ScopedHandle OpenDevice(const std::wstring& device_path); const scoped_refptr<base::SequencedTaskRunner> task_runner_; const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; diff --git a/chromium/services/device/nfc/android/BUILD.gn b/chromium/services/device/nfc/android/BUILD.gn index 882201e1f05..c2107663e88 100644 --- a/chromium/services/device/nfc/android/BUILD.gn +++ b/chromium/services/device/nfc/android/BUILD.gn @@ -29,5 +29,6 @@ android_library("java") { "//services/device/public/mojom:mojom_java", "//services/service_manager/public/java:service_manager_java", "//services/service_manager/public/mojom:mojom_java", + "//third_party/android_deps:androidx_annotation_annotation_java", ] } diff --git a/chromium/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java b/chromium/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java index c5a5a35df7e..1fc5cd3430f 100644 --- a/chromium/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java +++ b/chromium/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java @@ -5,7 +5,6 @@ package org.chromium.device.nfc; import android.Manifest; -import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; @@ -15,7 +14,6 @@ import android.nfc.NfcAdapter.ReaderCallback; import android.nfc.NfcManager; import android.nfc.Tag; import android.nfc.TagLostException; -import android.os.Build; import android.os.Process; import android.os.Vibrator; import android.util.SparseArray; @@ -413,7 +411,6 @@ public class NfcImpl implements Nfc { * discovered, Tag object is delegated to mojo service implementation method * NfcImpl.onTagDiscovered(). */ - @TargetApi(Build.VERSION_CODES.KITKAT) private static class ReaderCallbackHandler implements ReaderCallback { private final NfcImpl mNfcImpl; @@ -449,7 +446,6 @@ public class NfcImpl implements Nfc { * Disables reader mode. * @see android.nfc.NfcAdapter#disableReaderMode */ - @TargetApi(Build.VERSION_CODES.KITKAT) private void disableReaderMode() { // There is no API that could query whether reader mode is enabled for adapter. // If mReaderCallbackHandler is null, reader mode is not enabled. diff --git a/chromium/services/device/public/cpp/bluetooth/BUILD.gn b/chromium/services/device/public/cpp/bluetooth/BUILD.gn index cef8c766f6f..9f748a14742 100644 --- a/chromium/services/device/public/cpp/bluetooth/BUILD.gn +++ b/chromium/services/device/public/cpp/bluetooth/BUILD.gn @@ -11,6 +11,7 @@ source_set("bluetooth") { deps = [ "//base", "//device/bluetooth", + "//device/bluetooth/public/cpp:cpp", "//device/bluetooth/strings", "//services/device/public/mojom", "//ui/base", diff --git a/chromium/services/device/public/cpp/bluetooth/bluetooth_utils.cc b/chromium/services/device/public/cpp/bluetooth/bluetooth_utils.cc index d9fd4d260c0..5b85c093140 100644 --- a/chromium/services/device/public/cpp/bluetooth/bluetooth_utils.cc +++ b/chromium/services/device/public/cpp/bluetooth/bluetooth_utils.cc @@ -139,4 +139,11 @@ base::string16 GetBluetoothDeviceLabelForAccessibility( name_utf16); } +const BluetoothUUID& GetSerialPortProfileUUID() { + // The Serial Port Profile (SPP) UUID is 1101. + // https://chromium-review.googlesource.com/c/chromium/src/+/2334682/17..19 + static const BluetoothUUID kValue("1101"); + return kValue; +} + } // namespace device diff --git a/chromium/services/device/public/cpp/bluetooth/bluetooth_utils.h b/chromium/services/device/public/cpp/bluetooth/bluetooth_utils.h index adb92141431..2c882928867 100644 --- a/chromium/services/device/public/cpp/bluetooth/bluetooth_utils.h +++ b/chromium/services/device/public/cpp/bluetooth/bluetooth_utils.h @@ -6,6 +6,7 @@ #define SERVICES_DEVICE_PUBLIC_CPP_BLUETOOTH_BLUETOOTH_UTILS_H_ #include "base/strings/string16.h" +#include "device/bluetooth/public/cpp/bluetooth_uuid.h" #include "services/device/public/mojom/bluetooth_system.mojom.h" namespace device { @@ -25,6 +26,9 @@ base::string16 GetBluetoothDeviceNameForDisplay( base::string16 GetBluetoothDeviceLabelForAccessibility( const mojom::BluetoothDeviceInfoPtr& device_info); +// Returns a BluetoothUUID for a Bluetooth SPP device. +const BluetoothUUID& GetSerialPortProfileUUID(); + } // namespace device #endif // SERVICES_DEVICE_PUBLIC_CPP_BLUETOOTH_BLUETOOTH_UTILS_H_ diff --git a/chromium/services/device/public/cpp/hid/hid_device_filter_unittest.cc b/chromium/services/device/public/cpp/hid/hid_device_filter_unittest.cc index 8fb23c78f8f..9ce84e03bea 100644 --- a/chromium/services/device/public/cpp/hid/hid_device_filter_unittest.cc +++ b/chromium/services/device/public/cpp/hid/hid_device_filter_unittest.cc @@ -15,7 +15,7 @@ namespace device { namespace { -#if defined(OS_MACOSX) +#if defined(OS_MAC) const uint64_t kTestDeviceId = 42; #elif defined(OS_WIN) const wchar_t* kTestDeviceId = L"device1"; diff --git a/chromium/services/device/public/cpp/serial/BUILD.gn b/chromium/services/device/public/cpp/serial/BUILD.gn new file mode 100644 index 00000000000..686c2eb8d81 --- /dev/null +++ b/chromium/services/device/public/cpp/serial/BUILD.gn @@ -0,0 +1,10 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +static_library("switches") { + sources = [ + "serial_switches.cc", + "serial_switches.h", + ] +} diff --git a/chromium/services/device/public/cpp/serial/serial_switches.cc b/chromium/services/device/public/cpp/serial/serial_switches.cc new file mode 100644 index 00000000000..d6405bc1d11 --- /dev/null +++ b/chromium/services/device/public/cpp/serial/serial_switches.cc @@ -0,0 +1,13 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/public/cpp/serial/serial_switches.h" + +namespace switches { + +// Enable serial communication for SPP devices. +const char kEnableBluetoothSerialPortProfileInSerialApi[] = + "enable-bluetooth-spp-in-serial-api"; + +} // namespace switches diff --git a/chromium/services/device/public/cpp/serial/serial_switches.h b/chromium/services/device/public/cpp/serial/serial_switches.h new file mode 100644 index 00000000000..76f1298a8e7 --- /dev/null +++ b/chromium/services/device/public/cpp/serial/serial_switches.h @@ -0,0 +1,14 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_DEVICE_PUBLIC_CPP_SERIAL_SERIAL_SWITCHES_H_ +#define SERVICES_DEVICE_PUBLIC_CPP_SERIAL_SERIAL_SWITCHES_H_ + +namespace switches { + +extern const char kEnableBluetoothSerialPortProfileInSerialApi[]; + +} // namespace switches + +#endif // SERVICES_DEVICE_PUBLIC_CPP_SERIAL_SERIAL_SWITCHES_H_ diff --git a/chromium/services/device/public/cpp/usb/usb_utils.cc b/chromium/services/device/public/cpp/usb/usb_utils.cc index ef6f5199686..db364184fbf 100644 --- a/chromium/services/device/public/cpp/usb/usb_utils.cc +++ b/chromium/services/device/public/cpp/usb/usb_utils.cc @@ -26,6 +26,14 @@ bool UsbDeviceFilterMatches(const mojom::UsbDeviceFilter& filter, } if (filter.has_class_code) { + if (device_info.class_code == filter.class_code && + (!filter.has_subclass_code || + (device_info.subclass_code == filter.subclass_code && + (!filter.has_protocol_code || + device_info.protocol_code == filter.protocol_code)))) { + return true; + } + for (auto& config : device_info.configurations) { for (auto& iface : config->interfaces) { for (auto& alternate_info : iface->alternates) { diff --git a/chromium/services/device/public/cpp/usb/usb_utils_unittest.cc b/chromium/services/device/public/cpp/usb/usb_utils_unittest.cc index c79d56c51b7..a50ab515d54 100644 --- a/chromium/services/device/public/cpp/usb/usb_utils_unittest.cc +++ b/chromium/services/device/public/cpp/usb/usb_utils_unittest.cc @@ -168,6 +168,67 @@ TEST_F(UsbUtilsTest, MatchSerialNumber) { EXPECT_FALSE(UsbDeviceFilterMatches(*filter, GetPhoneInfo())); } +TEST_F(UsbUtilsTest, MatchDeviceClass) { + auto device_info = mojom::UsbDeviceInfo::New(); + device_info->class_code = 1; + device_info->subclass_code = 2; + device_info->protocol_code = 3; + + auto matching_class_filter = mojom::UsbDeviceFilter::New(); + matching_class_filter->has_class_code = true; + matching_class_filter->class_code = 1; + EXPECT_TRUE(UsbDeviceFilterMatches(*matching_class_filter, *device_info)); + + auto nonmatching_class_filter = mojom::UsbDeviceFilter::New(); + nonmatching_class_filter->has_class_code = true; + nonmatching_class_filter->class_code = 2; + EXPECT_FALSE(UsbDeviceFilterMatches(*nonmatching_class_filter, *device_info)); + + auto matching_subclass_filter = mojom::UsbDeviceFilter::New(); + matching_subclass_filter->has_class_code = true; + matching_subclass_filter->class_code = 1; + matching_subclass_filter->has_subclass_code = true; + matching_subclass_filter->subclass_code = 2; + EXPECT_TRUE(UsbDeviceFilterMatches(*matching_subclass_filter, *device_info)); + + auto nonmatching_subclass_filter = mojom::UsbDeviceFilter::New(); + nonmatching_subclass_filter->has_class_code = true; + nonmatching_subclass_filter->class_code = 1; + nonmatching_subclass_filter->has_subclass_code = true; + nonmatching_subclass_filter->subclass_code = 3; + EXPECT_FALSE( + UsbDeviceFilterMatches(*nonmatching_subclass_filter, *device_info)); + + auto matching_protocol_filter = mojom::UsbDeviceFilter::New(); + matching_protocol_filter->has_class_code = true; + matching_protocol_filter->class_code = 1; + matching_protocol_filter->has_subclass_code = true; + matching_protocol_filter->subclass_code = 2; + matching_protocol_filter->has_protocol_code = true; + matching_protocol_filter->protocol_code = 3; + EXPECT_TRUE(UsbDeviceFilterMatches(*matching_protocol_filter, *device_info)); + + auto nonmatching_protocol_filter = mojom::UsbDeviceFilter::New(); + nonmatching_protocol_filter->has_class_code = true; + nonmatching_protocol_filter->class_code = 1; + nonmatching_protocol_filter->has_subclass_code = true; + nonmatching_protocol_filter->subclass_code = 2; + nonmatching_protocol_filter->has_protocol_code = true; + nonmatching_protocol_filter->protocol_code = 1; + EXPECT_FALSE( + UsbDeviceFilterMatches(*nonmatching_protocol_filter, *device_info)); + + // Without |has_subclass_code| set the |protocol_code| filter should be + // ignored. + auto invalid_matching_protocol_filter = mojom::UsbDeviceFilter::New(); + invalid_matching_protocol_filter->has_class_code = true; + invalid_matching_protocol_filter->class_code = 1; + invalid_matching_protocol_filter->has_protocol_code = true; + invalid_matching_protocol_filter->protocol_code = 2; + EXPECT_TRUE( + UsbDeviceFilterMatches(*invalid_matching_protocol_filter, *device_info)); +} + TEST_F(UsbUtilsTest, MatchAnyEmptyList) { std::vector<mojom::UsbDeviceFilterPtr> filters; ASSERT_TRUE(UsbDeviceFilterMatchesAny(filters, GetPhoneInfo())); diff --git a/chromium/services/device/public/mojom/BUILD.gn b/chromium/services/device/public/mojom/BUILD.gn index 221576f2369..e7f0f733d41 100644 --- a/chromium/services/device/public/mojom/BUILD.gn +++ b/chromium/services/device/public/mojom/BUILD.gn @@ -86,7 +86,7 @@ mojom("device_service") { disable_variants = true enabled_features = [] - if (is_linux && use_udev) { + if ((is_linux || is_chromeos) && use_udev) { enabled_features += [ "enable_input_device_manager" ] } diff --git a/chromium/services/device/public/mojom/hid.mojom b/chromium/services/device/public/mojom/hid.mojom index 1631214c0f4..cc418d198ee 100644 --- a/chromium/services/device/public/mojom/hid.mojom +++ b/chromium/services/device/public/mojom/hid.mojom @@ -132,8 +132,8 @@ const uint32 kHIDCollectionTypeVendorMin = 0x80; const uint32 kHIDCollectionTypeVendorMax = 0xff; struct HidUsageAndPage { - uint16 usage; - uint16 usage_page; + uint16 usage@0; + uint16 usage_page@1; }; // A HID report is a packet of data sent between a HID device and its host. The @@ -149,50 +149,50 @@ struct HidUsageAndPage { struct HidReportItem { // True if the usages for this item are defined by |usage_minimum| and // |usage_maximum|. False if the usages for this item are defined by |usages|. - bool is_range; + bool is_range@0; // Data associated with the Main item. See section 6.2.2.5 of the Device Class // Definition for HID. // https://www.usb.org/sites/default/files/documents/hid1_11.pdf - bool is_constant; // Constant (true) or Data (false). - bool is_variable; // Variable (true) or Array (false). - bool is_relative; // Relative (true) or Absolute (false). - bool wrap; // Wrap (true) or No Wrap (false). - bool is_non_linear; // Non Linear (true) or Linear (false). - bool no_preferred_state; // No Preferred (true) or Preferred State (false). - bool has_null_position; // Null state (true) or No Null position (false). - bool is_volatile; // Volatile (true) or Non Volatile (false). - bool is_buffered_bytes; // Buffered Bytes (true) or Bit Field (false). + bool is_constant@1; // Constant (true) or Data (false). + bool is_variable@2; // Variable (true) or Array (false). + bool is_relative@3; // Relative (true) or Absolute (false). + bool wrap@4; // Wrap (true) or No Wrap (false). + bool is_non_linear@5; // Non Linear (true) or Linear (false). + bool no_preferred_state@6; // No Preferred (true) or Preferred State (false). + bool has_null_position@7; // Null state (true) or No Null position (false). + bool is_volatile@8; // Volatile (true) or Non Volatile (false). + bool is_buffered_bytes@9; // Buffered Bytes (true) or Bit Field (false). // Local items. See section 6.2.2.8 of the Device Class Definition for HID. // https://www.usb.org/sites/default/files/documents/hid1_11.pdf // If |is_range| is false, usages for this item are listed in |usages| in the // order they were encountered in the report descriptor. - array<HidUsageAndPage> usages; + array<HidUsageAndPage> usages@10; // If |is_range| is true, usages for this item are assigned from a range of // usages starting at |usage_minimum| and incrementing until |usage_maximum|. // If this item is a Variable and |report_count| is larger than the number of // usages in this range, all remaining fields are also assigned // |usage_maximum|. - HidUsageAndPage usage_minimum; - HidUsageAndPage usage_maximum; + HidUsageAndPage usage_minimum@11; + HidUsageAndPage usage_maximum@12; // If this item has one or more entries in the Physical descriptor table, // |designator_minimum| and |designator_maximum| are set to the minimum and // maximum indices of these entries. If the item has no designators, both are // set to zero. A designator describes the body part intended to be used with // a particular control. - uint32 designator_minimum; - uint32 designator_maximum; + uint32 designator_minimum@13; + uint32 designator_maximum@14; // If this item has one or more entries in the String descriptor table, // |string_minimum| and |string_maximum| are set to the minimum and maximum // indices of these entries. If the item has no strings, both are set to zero. // The String descriptor contains a list of text strings for the device. - uint32 string_minimum; - uint32 string_maximum; + uint32 string_minimum@15; + uint32 string_maximum@16; // Global items. See section 6.2.2.7 of the Device Class Definition for HID. // https://www.usb.org/sites/default/files/documents/hid1_11.pdf @@ -200,32 +200,32 @@ struct HidReportItem { // |logical_minimum| and |logical_maximum| define the extent of valid data // values for the item in logical units. If |has_null_position| is true, // values outside this range are interpreted as null input. - int32 logical_minimum; - int32 logical_maximum; + int32 logical_minimum@17; + int32 logical_maximum@18; // |physical_minimum| and |physical_maximum| define the extent of valid data // values after applying units to the logical extents. - int32 physical_minimum; - int32 physical_maximum; + int32 physical_minimum@19; + int32 physical_maximum@20; // The value of the unit exponent in base 10. Values between 0x0 and 0x7 // represent positive exponents 0 to 7, values between 0x8 and 0xF represent // nevative exponents -8 to -1. Bits [4:31] are reserved and should be set to // zero. - uint32 unit_exponent; + uint32 unit_exponent@21; // The units to apply to this item. The |unit| value is coded as seven 4-bit // fields that define the unit system and the exponents on units of length, // mass, time, temperature, current, and luminous intensity. Bits [28:31] are // reserved and should be set to zero. - uint32 unit; + uint32 unit@21; // A single report item may define multiple same-sized fields within a report. // |report_size| and |report_count| define the size of one field (in bits) and // the number of fields within the item. The total size of this item in bits // is equal to the product of these values. - uint32 report_size; - uint32 report_count; + uint32 report_size@22; + uint32 report_count@23; }; // Contains information collected from the HID report descriptor regarding a @@ -237,10 +237,10 @@ struct HidReportItem { struct HidReportDescription { // Report ID associated with this report, or zero if the device does not use // report IDs. - uint8 report_id; + uint8 report_id@0; // The sequence of report items that describe this report. - array<HidReportItem> items; + array<HidReportItem> items@1; }; // Contains information collected from the HID report descriptor regarding a @@ -254,26 +254,26 @@ struct HidReportDescription { // https://www.usb.org/sites/default/files/documents/hid1_11.pdf struct HidCollectionInfo { // Collection's usage ID. - HidUsageAndPage usage; + HidUsageAndPage usage@0; // HID report IDs which belong to this collection or to its embedded // collections, in the order they appear in the report descriptor. - array<uint8> report_ids; + array<uint8> report_ids@1; // Collection type. - uint32 collection_type; + uint32 collection_type@2; // Reports described in the report descriptor. - array<HidReportDescription> input_reports; - array<HidReportDescription> output_reports; - array<HidReportDescription> feature_reports; + array<HidReportDescription> input_reports@3; + array<HidReportDescription> output_reports@4; + array<HidReportDescription> feature_reports@5; // The children of this collection in the order they appear in the report // descriptor. In child collections, the reports described in the // |input_reports|, |output_reports|, and |feature_reports| members include // only the subsequence of report items from the parent collection that appear // within the child collection. - array<HidCollectionInfo> children; + array<HidCollectionInfo> children@6; }; // Contains information related to a single logical HID device. Note that a @@ -284,65 +284,65 @@ struct HidDeviceInfo { // A random GUID assigned to the device during enumeration. The device GUID is // stable as long as the application is running and the device remains // connected to the system. - string guid; + string guid@0; // A platform-specific string identifier for the physical device. When a // single physical device exposes multiple logical devices, the logical // devices will have the same value for |physical_device_id|. - string physical_device_id; + string physical_device_id@1; // The vendor ID value reported by the device. Vendor IDs are 16-bit values // assigned by the USB-IF or Bluetooth SIG to manufacturers of USB and // Bluetooth devices. // TODO(mattreynolds): Indicate whether the vendor ID was assigned by USB-IF // or Bluetooth SIG. - uint16 vendor_id; + uint16 vendor_id@2; // The product ID reported by the device. Product IDs are 16-bit values // assigned by the manufacturer to identify a particular device model. - uint16 product_id; + uint16 product_id@3; // The product name string reported by the device, or an empty string if no // product name is available. - string product_name; + string product_name@4; // The USB serial number string, or an empty string if the device is not a USB // device or has no serial number. - string serial_number; + string serial_number@5; // The bus used to connect this device to the system. - HidBusType bus_type; + HidBusType bus_type@6; // An array of bytes representing the HID report descriptor reported by the // device, or an empty array if the report descriptor could not be read. - array<uint8> report_descriptor; + array<uint8> report_descriptor@7; // A structured representation of the information contained in the HID report // descriptor. - array<HidCollectionInfo> collections; + array<HidCollectionInfo> collections@8; // True if the device uses report IDs. - bool has_report_id; + bool has_report_id@9; // The maximum size in bytes of input, output, and feature reports supported // by the device. - uint64 max_input_report_size; - uint64 max_output_report_size; - uint64 max_feature_report_size; + uint64 max_input_report_size@10; + uint64 max_output_report_size@11; + uint64 max_feature_report_size@12; // A platform-specific string identifier for the logical device. - string device_node; + string device_node@13; }; // A client interface for receiving a notification when HID devices are // physically connected or disconnected. interface HidManagerClient { // Notifies the client that a device is added. - DeviceAdded(HidDeviceInfo device_info); + DeviceAdded@0(HidDeviceInfo device_info); // Notifies the client that a device is being removed; called before // removing the device from HidService. - DeviceRemoved(HidDeviceInfo device_info); + DeviceRemoved@1(HidDeviceInfo device_info); }; // Provides an interface for enumerating available HID devices, registering for @@ -352,11 +352,11 @@ interface HidManager { // Enumerates available devices and set as a client of HidManager. // The implementation of HidManager guarantees that the returned callback // will always be posted earlier than DeviceAdded() and DeviceRemoved(). - GetDevicesAndSetClient(pending_associated_remote<HidManagerClient> client) => - (array<HidDeviceInfo> devices); + GetDevicesAndSetClient@0(pending_associated_remote<HidManagerClient> client) + => (array<HidDeviceInfo> devices); // Enumerates available devices only. - GetDevices() => (array<HidDeviceInfo> devices); + GetDevices@1() => (array<HidDeviceInfo> devices); // Opens a connection to a device by given guid. The callback will be run // with null on failure. @@ -368,7 +368,7 @@ interface HidManager { // open. When the HID connection is closed, the watcher is also closed. This // is useful when the connection closure should be handled somewhere other // than where the |connection| and |connection_client| are held. - Connect(string device_guid, + Connect@2(string device_guid, pending_remote<HidConnectionClient>? connection_client, pending_remote<HidConnectionWatcher>? watcher) => (pending_remote<HidConnection>? connection); @@ -381,18 +381,18 @@ interface HidManager { interface HidConnection { // A |report_id| of 0 is returned via callback if report IDs are not // supported by the device. - Read() => (bool success, uint8 report_id, array<uint8>? buffer); + Read@0() => (bool success, uint8 report_id, array<uint8>? buffer); // Pass the |report_id| as 0 if not supported by the device. - Write(uint8 report_id, array<uint8> buffer) => (bool success); + Write@1(uint8 report_id, array<uint8> buffer) => (bool success); // The buffer will contain whatever report data was received from the device. // This may include the report ID. The report ID is not stripped because a // device may respond with other data in place of the report ID. - GetFeatureReport(uint8 report_id) => (bool success, array<uint8>? buffer); + GetFeatureReport@2(uint8 report_id) => (bool success, array<uint8>? buffer); // Pass the |report_id| as 0 if not supported by the device. - SendFeatureReport(uint8 report_id, array<uint8> buffer) => (bool success); + SendFeatureReport@3(uint8 report_id, array<uint8> buffer) => (bool success); }; // A client interface for receiving a notification when input reports are @@ -400,7 +400,7 @@ interface HidConnection { interface HidConnectionClient { // Notifies the client that an input report was received. A |report_id| of 0 // is passed if report IDs are not used by the device. - OnInputReport(uint8 report_id, array<uint8> buffer); + OnInputReport@0(uint8 report_id, array<uint8> buffer); }; // A client interface for observing whether a HID connection is still active. diff --git a/chromium/services/device/public/mojom/serial.mojom b/chromium/services/device/public/mojom/serial.mojom index b24628c54ee..905ea45b9cc 100644 --- a/chromium/services/device/public/mojom/serial.mojom +++ b/chromium/services/device/public/mojom/serial.mojom @@ -16,6 +16,10 @@ struct SerialPortInfo { mojo_base.mojom.FilePath path; + // This member is used to identify whether the SerialPortInfo object is + // converted from a Bluetooth serial device. + DeviceType type; + // On macOS a serial device may have two paths, one for the call-out device // and one for the dial-in device. The call-out device is preferred. If // there is also an associated dial-in device its path is provided here. If @@ -66,6 +70,27 @@ enum SerialStopBits { TWO, }; +enum SerialPortFlushMode { + // Flushes both receive and transmit buffers without discarding any bytes in + // the data pipes. This is for compatibility with chrome.serial.flush(). + kReceiveAndTransmit, + + // Flushes the receive buffers and discards data in the data_pipe_producer by + // closing it. + kReceive, + + // Flushes the send buffers and discards data in the data_pipe_consumer by + // closing it. + kTransmit, +}; + +enum DeviceType { + // The SerialPortInfo object is created from a serial device. + PLATFORM_SERIAL, + // The SerialPortInfo object is created from a Bluetooth SPP device. + SPP_DEVICE, +}; + struct SerialConnectionOptions { uint32 bitrate = 0; SerialDataBits data_bits = NONE; @@ -146,8 +171,12 @@ interface SerialPort { // called on |client| to indicate an error. StartReading(handle<data_pipe_producer> producer); - // Flushes input and output buffers. - Flush() => (bool success); + // Flushes buffers according to the selected |mode|. + Flush(SerialPortFlushMode mode) => (); + + // Waits for the data_pipe_consumer passed to StartWriting() to be closed and + // all buffered data to be transmitted by the port. + Drain() => (); // Reads current control signals (DCD, CTS, etc.). GetControlSignals() => (SerialPortControlSignals signals); diff --git a/chromium/services/device/screen_orientation/screen_orientation_listener_android.cc b/chromium/services/device/screen_orientation/screen_orientation_listener_android.cc index 0b36405e97c..40b87982aa3 100644 --- a/chromium/services/device/screen_orientation/screen_orientation_listener_android.cc +++ b/chromium/services/device/screen_orientation/screen_orientation_listener_android.cc @@ -6,7 +6,7 @@ #include "base/android/jni_android.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop_current.h" +#include "base/task/current_thread.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "services/device/screen_orientation/screen_orientation_jni_headers/ScreenOrientationListener_jni.h" @@ -23,7 +23,7 @@ void ScreenOrientationListenerAndroid::Create( ScreenOrientationListenerAndroid::ScreenOrientationListenerAndroid() = default; ScreenOrientationListenerAndroid::~ScreenOrientationListenerAndroid() { - DCHECK(base::MessageLoopCurrentForIO::IsSet()); + DCHECK(base::CurrentIOThread::IsSet()); } void ScreenOrientationListenerAndroid::IsAutoRotateEnabledByUser( diff --git a/chromium/services/device/serial/BUILD.gn b/chromium/services/device/serial/BUILD.gn index d75328bfb9b..77a58d170f6 100644 --- a/chromium/services/device/serial/BUILD.gn +++ b/chromium/services/device/serial/BUILD.gn @@ -4,7 +4,7 @@ import("//build/config/features.gni") -if (is_win || (is_linux && use_udev) || is_mac) { +if (is_win || ((is_linux || is_chromeos) && use_udev) || is_mac) { config("platform_support") { visibility = [ ":serial" ] if (is_win) { @@ -21,6 +21,10 @@ if (is_win || (is_linux && use_udev) || is_mac) { ] sources = [ + "bluetooth_serial_device_enumerator.cc", + "bluetooth_serial_device_enumerator.h", + "bluetooth_serial_port_impl.cc", + "bluetooth_serial_port_impl.h", "buffer.cc", "buffer.h", "serial_device_enumerator.cc", @@ -47,8 +51,12 @@ if (is_win || (is_linux && use_udev) || is_mac) { deps = [ "//base", + "//device/bluetooth:bluetooth", + "//device/bluetooth/public/cpp", "//mojo/public/cpp/bindings", "//net", + "//services/device/public/cpp/bluetooth:bluetooth", + "//services/device/public/cpp/serial:switches", ] if (is_posix) { @@ -67,7 +75,7 @@ if (is_win || (is_linux && use_udev) || is_mac) { } if (is_mac) { - libs = [ + frameworks = [ "Foundation.framework", "IOKit.framework", ] diff --git a/chromium/services/device/serial/bluetooth_serial_device_enumerator.cc b/chromium/services/device/serial/bluetooth_serial_device_enumerator.cc new file mode 100644 index 00000000000..07359674ce6 --- /dev/null +++ b/chromium/services/device/serial/bluetooth_serial_device_enumerator.cc @@ -0,0 +1,68 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/serial/bluetooth_serial_device_enumerator.h" + +#include "base/command_line.h" +#include "base/unguessable_token.h" +#include "device/bluetooth/bluetooth_adapter_factory.h" +#include "services/device/public/cpp/bluetooth/bluetooth_utils.h" +#include "services/device/public/cpp/serial/serial_switches.h" +#include "services/device/public/mojom/serial.mojom.h" + +namespace device { + +BluetoothSerialDeviceEnumerator::BluetoothSerialDeviceEnumerator() { + DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBluetoothSerialPortProfileInSerialApi)); + device::BluetoothAdapterFactory::Get()->GetClassicAdapter( + base::BindOnce(&BluetoothSerialDeviceEnumerator::OnGotClassicAdapter, + base::Unretained(this))); +} + +BluetoothSerialDeviceEnumerator::~BluetoothSerialDeviceEnumerator() = default; + +void BluetoothSerialDeviceEnumerator::OnGotClassicAdapter( + scoped_refptr<device::BluetoothAdapter> adapter) { + DCHECK(adapter); + adapter_ = adapter; + adapter_->AddObserver(this); + BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); + for (auto* device : devices) { + BluetoothDevice::UUIDSet device_uuids = device->GetUUIDs(); + if (base::Contains(device_uuids, GetSerialPortProfileUUID())) { + auto port = mojom::SerialPortInfo::New(); + port->token = base::UnguessableToken::Create(); + port->path = base::FilePath::FromUTF8Unsafe(device->GetIdentifier()); + port->type = mojom::DeviceType::SPP_DEVICE; + bluetooth_ports_.insert( + std::make_pair(device->GetAddress(), port->token)); + AddPort(std::move(port)); + } + } +} + +void BluetoothSerialDeviceEnumerator::DeviceAdded(BluetoothAdapter* adapter, + BluetoothDevice* device) { + BluetoothDevice::UUIDSet device_uuids = device->GetUUIDs(); + if (base::Contains(device_uuids, GetSerialPortProfileUUID())) { + auto port = mojom::SerialPortInfo::New(); + port->token = base::UnguessableToken::Create(); + port->path = base::FilePath::FromUTF8Unsafe(device->GetIdentifier()); + port->type = mojom::DeviceType::SPP_DEVICE; + bluetooth_ports_.insert(std::make_pair(device->GetAddress(), port->token)); + AddPort(std::move(port)); + } +} + +void BluetoothSerialDeviceEnumerator::DeviceRemoved(BluetoothAdapter* adapter, + BluetoothDevice* device) { + auto it = bluetooth_ports_.find(device->GetAddress()); + DCHECK(it != bluetooth_ports_.end()); + base::UnguessableToken token = it->second; + bluetooth_ports_.erase(it); + RemovePort(token); +} + +} // namespace device diff --git a/chromium/services/device/serial/bluetooth_serial_device_enumerator.h b/chromium/services/device/serial/bluetooth_serial_device_enumerator.h new file mode 100644 index 00000000000..fa3928b8fa4 --- /dev/null +++ b/chromium/services/device/serial/bluetooth_serial_device_enumerator.h @@ -0,0 +1,43 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_DEVICE_SERIAL_BLUETOOTH_SERIAL_DEVICE_ENUMERATOR_H_ +#define SERVICES_DEVICE_SERIAL_BLUETOOTH_SERIAL_DEVICE_ENUMERATOR_H_ + +#include <map> + +#include "base/memory/scoped_refptr.h" +#include "device/bluetooth/bluetooth_adapter.h" +#include "services/device/public/mojom/serial.mojom-forward.h" +#include "services/device/serial/serial_device_enumerator.h" + +namespace device { + +class BluetoothSerialDeviceEnumerator : public BluetoothAdapter::Observer, + public SerialDeviceEnumerator { + public: + BluetoothSerialDeviceEnumerator(); + BluetoothSerialDeviceEnumerator(const BluetoothSerialDeviceEnumerator&) = + delete; + BluetoothSerialDeviceEnumerator& operator=( + const BluetoothSerialDeviceEnumerator&) = delete; + ~BluetoothSerialDeviceEnumerator() override; + + // BluetoothAdapter::Observer methods: + void DeviceAdded(BluetoothAdapter* adapter, BluetoothDevice* device) override; + void DeviceRemoved(BluetoothAdapter* adapter, + BluetoothDevice* device) override; + + protected: + scoped_refptr<BluetoothAdapter> adapter_; + + private: + void OnGotClassicAdapter(scoped_refptr<device::BluetoothAdapter> adapter); + + std::unordered_map<std::string, base::UnguessableToken> bluetooth_ports_; +}; + +} // namespace device + +#endif // SERVICES_DEVICE_SERIAL_BLUETOOTH_SERIAL_DEVICE_ENUMERATOR_H_ diff --git a/chromium/services/device/serial/bluetooth_serial_port_impl.cc b/chromium/services/device/serial/bluetooth_serial_port_impl.cc new file mode 100644 index 00000000000..346a44ae5b8 --- /dev/null +++ b/chromium/services/device/serial/bluetooth_serial_port_impl.cc @@ -0,0 +1,382 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/serial/bluetooth_serial_port_impl.h" + +#include "base/command_line.h" +#include "net/base/io_buffer.h" +#include "services/device/public/cpp/bluetooth/bluetooth_utils.h" +#include "services/device/public/cpp/serial/serial_switches.h" +#include "services/device/serial/buffer.h" + +namespace device { + +// static +void BluetoothSerialPortImpl::Create( + std::unique_ptr<BluetoothDevice> device, + mojo::PendingReceiver<mojom::SerialPort> receiver, + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher) { + DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBluetoothSerialPortProfileInSerialApi)); + + // This BluetoothSerialPortImpl is owned by |receiver| and |watcher|. + new BluetoothSerialPortImpl(std::move(device), std::move(receiver), + std::move(watcher)); +} + +BluetoothSerialPortImpl::BluetoothSerialPortImpl( + std::unique_ptr<BluetoothDevice> device, + mojo::PendingReceiver<mojom::SerialPort> receiver, + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher) + : receiver_(this, std::move(receiver)), + watcher_(std::move(watcher)), + in_stream_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL), + out_stream_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL), + bluetooth_device_(std::move(device)) { + receiver_.set_disconnect_handler( + base::BindOnce([](BluetoothSerialPortImpl* self) { delete self; }, + base::Unretained(this))); + if (watcher_.is_bound()) { + watcher_.set_disconnect_handler( + base::BindOnce([](BluetoothSerialPortImpl* self) { delete self; }, + base::Unretained(this))); + } +} + +BluetoothSerialPortImpl::~BluetoothSerialPortImpl() { + if (bluetooth_socket_) + bluetooth_socket_->Close(); +} + +void BluetoothSerialPortImpl::Open( + mojom::SerialConnectionOptionsPtr options, + mojo::PendingRemote<mojom::SerialPortClient> client, + OpenCallback callback) { + if (client) + client_.Bind(std::move(client)); + + BluetoothDevice::UUIDSet device_uuids = bluetooth_device_->GetUUIDs(); + if (base::Contains(device_uuids, GetSerialPortProfileUUID())) { + auto copyable_callback = + base::AdaptCallbackForRepeating(std::move(callback)); + bluetooth_device_->ConnectToService( + GetSerialPortProfileUUID(), + base::BindOnce(&BluetoothSerialPortImpl::OnSocketConnected, + weak_ptr_factory_.GetWeakPtr(), copyable_callback), + base::BindOnce(&BluetoothSerialPortImpl::OnSocketConnectedError, + weak_ptr_factory_.GetWeakPtr(), copyable_callback)); + return; + } + std::move(callback).Run(false); +} + +void BluetoothSerialPortImpl::OnSocketConnected( + OpenCallback callback, + scoped_refptr<BluetoothSocket> socket) { + DCHECK(socket); + bluetooth_socket_ = std::move(socket); + std::move(callback).Run(true); +} + +void BluetoothSerialPortImpl::OnSocketConnectedError( + OpenCallback callback, + const std::string& message) { + std::move(callback).Run(false); +} + +void BluetoothSerialPortImpl::StartWriting( + mojo::ScopedDataPipeConsumerHandle consumer) { + DCHECK(!write_pending_); + + if (in_stream_) { + mojo::ReportBadMessage("Data pipe consumer still open."); + return; + } + + if (!bluetooth_socket_) { + mojo::ReportBadMessage("No Bluetooth socket."); + return; + } + + in_stream_watcher_.Cancel(); + in_stream_ = std::move(consumer); + in_stream_watcher_.Watch( + in_stream_.get(), + MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + base::BindRepeating(&BluetoothSerialPortImpl::WriteToSocket, + weak_ptr_factory_.GetWeakPtr())); + in_stream_watcher_.ArmOrNotify(); +} + +void BluetoothSerialPortImpl::StartReading( + mojo::ScopedDataPipeProducerHandle producer) { + if (out_stream_) { + mojo::ReportBadMessage("Data pipe producer still open."); + return; + } + + if (!bluetooth_socket_) { + mojo::ReportBadMessage("No Bluetooth socket."); + return; + } + + out_stream_watcher_.Cancel(); + out_stream_ = std::move(producer); + out_stream_watcher_.Watch( + out_stream_.get(), + MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + base::BindRepeating(&BluetoothSerialPortImpl::ReadFromSocketAndWriteOut, + weak_ptr_factory_.GetWeakPtr())); + out_stream_watcher_.ArmOrNotify(); +} + +void BluetoothSerialPortImpl::ReadFromSocketAndWriteOut( + MojoResult result, + const mojo::HandleSignalsState& state) { + switch (result) { + case MOJO_RESULT_OK: + ReadMore(); + break; + case MOJO_RESULT_SHOULD_WAIT: + // If there is no space to write, wait for more space. + out_stream_watcher_.ArmOrNotify(); + break; + case MOJO_RESULT_FAILED_PRECONDITION: + case MOJO_RESULT_CANCELLED: + // The |out_stream_| has been closed. + out_stream_watcher_.Cancel(); + out_stream_.reset(); + break; + default: + NOTREACHED() << "Unexpected Mojo result: " << result; + } +} + +void BluetoothSerialPortImpl::ReadMore() { + DCHECK(out_stream_.is_valid()); + + void* buffer = nullptr; + + uint32_t buffer_max_size = 0; + // The |buffer| is owned by |out_stream_|. + MojoResult result = out_stream_->BeginWriteData(&buffer, &buffer_max_size, + MOJO_WRITE_DATA_FLAG_NONE); + if (result == MOJO_RESULT_SHOULD_WAIT) { + out_stream_watcher_.ArmOrNotify(); + return; + } + if (result != MOJO_RESULT_OK) { + out_stream_watcher_.Cancel(); + out_stream_.reset(); + return; + } + + if (!bluetooth_socket_) { + mojo::ReportBadMessage("No Bluetooth socket."); + return; + } + + read_pending_ = true; + bluetooth_socket_->Receive( + buffer_max_size, + base::BindOnce( + &BluetoothSerialPortImpl::OnBluetoothSocketReceive, + weak_ptr_factory_.GetWeakPtr(), + base::make_span(reinterpret_cast<char*>(buffer), buffer_max_size)), + base::BindOnce(&BluetoothSerialPortImpl::OnBluetoothSocketReceiveError, + weak_ptr_factory_.GetWeakPtr())); +} + +void BluetoothSerialPortImpl::OnBluetoothSocketReceive( + base::span<char> pending_write_buffer, + int num_bytes_received, + scoped_refptr<net::IOBuffer> io_buffer) { + DCHECK_GT(num_bytes_received, 0); + DCHECK(io_buffer->data()); + DCHECK(out_stream_.is_valid()); + + read_pending_ = false; + std::copy(io_buffer->data(), io_buffer->data() + num_bytes_received, + pending_write_buffer.data()); + out_stream_->EndWriteData(static_cast<uint32_t>(num_bytes_received)); + + if (read_flush_callback_) { + std::move(read_flush_callback_).Run(); + out_stream_->EndWriteData(0); + out_stream_watcher_.Cancel(); + out_stream_.reset(); + return; + } + ReadMore(); +} + +void BluetoothSerialPortImpl::OnBluetoothSocketReceiveError( + BluetoothSocket::ErrorReason error_reason, + const std::string& error_message) { + DCHECK(out_stream_.is_valid()); + read_pending_ = false; + if (client_) { + DCHECK(error_reason != BluetoothSocket::ErrorReason::kIOPending); + switch (error_reason) { + case BluetoothSocket::ErrorReason::kDisconnected: + client_->OnReadError(mojom::SerialReceiveError::DISCONNECTED); + break; + case BluetoothSocket::ErrorReason::kIOPending: + NOTREACHED(); + break; + case BluetoothSocket::ErrorReason::kSystemError: + client_->OnReadError(mojom::SerialReceiveError::SYSTEM_ERROR); + break; + } + } + if (read_flush_callback_) + std::move(read_flush_callback_).Run(); + out_stream_->EndWriteData(0); + out_stream_watcher_.Cancel(); + out_stream_.reset(); +} + +void BluetoothSerialPortImpl::WriteToSocket( + MojoResult result, + const mojo::HandleSignalsState& state) { + switch (result) { + case MOJO_RESULT_OK: + WriteMore(); + break; + case MOJO_RESULT_SHOULD_WAIT: + // If there is no space to write, wait for more space. + in_stream_watcher_.ArmOrNotify(); + break; + case MOJO_RESULT_FAILED_PRECONDITION: + case MOJO_RESULT_CANCELLED: + // The |in_stream_| has been closed. + in_stream_watcher_.Cancel(); + in_stream_.reset(); + + if (drain_callback_) + std::move(drain_callback_).Run(); + break; + default: + NOTREACHED() << "Unexpected Mojo result: " << result; + } +} + +void BluetoothSerialPortImpl::WriteMore() { + DCHECK(in_stream_.is_valid()); + + const void* buffer = nullptr; + uint32_t buffer_size = 0; + // |buffer| is owned by |in_stream_|. + MojoResult result = in_stream_->BeginReadData(&buffer, &buffer_size, + MOJO_WRITE_DATA_FLAG_NONE); + if (result == MOJO_RESULT_SHOULD_WAIT) { + in_stream_watcher_.ArmOrNotify(); + return; + } + if (result != MOJO_RESULT_OK) { + in_stream_watcher_.Cancel(); + in_stream_.reset(); + return; + } + + if (!bluetooth_socket_) { + mojo::ReportBadMessage("No Bluetooth socket."); + return; + } + + write_pending_ = true; + bluetooth_socket_->Send( + base::MakeRefCounted<net::WrappedIOBuffer>( + reinterpret_cast<const char*>(buffer)), + buffer_size, + base::BindOnce(&BluetoothSerialPortImpl::OnBluetoothSocketSend, + weak_ptr_factory_.GetWeakPtr()), + base::BindOnce(&BluetoothSerialPortImpl::OnBluetoothSocketSendError, + weak_ptr_factory_.GetWeakPtr())); +} + +void BluetoothSerialPortImpl::OnBluetoothSocketSend(int num_bytes_sent) { + DCHECK_GE(num_bytes_sent, 0); + DCHECK(in_stream_.is_valid()); + + write_pending_ = false; + + in_stream_->EndReadData(static_cast<uint32_t>(num_bytes_sent)); + + if (write_flush_callback_) { + std::move(write_flush_callback_).Run(); + in_stream_->EndReadData(0); + in_stream_watcher_.Cancel(); + in_stream_.reset(); + return; + } + WriteMore(); +} + +void BluetoothSerialPortImpl::OnBluetoothSocketSendError( + const std::string& error_message) { + DCHECK(in_stream_.is_valid()); + write_pending_ = false; + if (client_) + client_->OnSendError(mojom::SerialSendError::SYSTEM_ERROR); + if (write_flush_callback_) + std::move(write_flush_callback_).Run(); + in_stream_->EndReadData(0); + in_stream_watcher_.Cancel(); + in_stream_.reset(); +} + +void BluetoothSerialPortImpl::Flush(mojom::SerialPortFlushMode mode, + FlushCallback callback) { + NOTIMPLEMENTED(); +} + +void BluetoothSerialPortImpl::Drain(DrainCallback callback) { + if (!in_stream_) { + std::move(callback).Run(); + return; + } + + drain_callback_ = std::move(callback); +} + +void BluetoothSerialPortImpl::GetControlSignals( + GetControlSignalsCallback callback) { + auto signals = mojom::SerialPortControlSignals::New(); + std::move(callback).Run(std::move(signals)); +} + +void BluetoothSerialPortImpl::SetControlSignals( + mojom::SerialHostControlSignalsPtr signals, + SetControlSignalsCallback callback) { + std::move(callback).Run(true); +} + +void BluetoothSerialPortImpl::ConfigurePort( + mojom::SerialConnectionOptionsPtr options, + ConfigurePortCallback callback) { + options_ = std::move(options); + std::move(callback).Run(true); +} + +void BluetoothSerialPortImpl::GetPortInfo(GetPortInfoCallback callback) { + auto info = mojom::SerialConnectionInfo::New( + /*bitrate=*/options_->bitrate, /*data_bits=*/options_->data_bits, + /*parity_bit=*/options_->parity_bit, /*stop_bits=*/options_->stop_bits, + /*cts_flow_control=*/options_->cts_flow_control); + std::move(callback).Run(std::move(info)); +} + +void BluetoothSerialPortImpl::Close(CloseCallback callback) { + client_.reset(); + if (bluetooth_socket_) { + bluetooth_socket_->Close(); + bluetooth_socket_.reset(); + } + std::move(callback).Run(); +} + +} // namespace device diff --git a/chromium/services/device/serial/bluetooth_serial_port_impl.h b/chromium/services/device/serial/bluetooth_serial_port_impl.h new file mode 100644 index 00000000000..107d3de93cb --- /dev/null +++ b/chromium/services/device/serial/bluetooth_serial_port_impl.h @@ -0,0 +1,107 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_DEVICE_SERIAL_BLUETOOTH_SERIAL_PORT_IMPL_H_ +#define SERVICES_DEVICE_SERIAL_BLUETOOTH_SERIAL_PORT_IMPL_H_ + +#include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluetooth_socket.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "services/device/public/mojom/serial.mojom.h" +#include "services/device/serial/serial_io_handler.h" +#include "services/device/serial/serial_port_impl.h" + +namespace device { + +// This class is intended to allow serial communication using a Bluetooth +// SPP device. The Bluetooth device is used to create a Bluetooth socket +// which is closed upon error in any of the interface functions. +class BluetoothSerialPortImpl : public mojom::SerialPort { + public: + // Creates of instance of BluetoothSerialPortImpl using a Bluetooth + // device and a receiver/watcher to create a pipe. The receiver and + // watcher will own this object. + static void Create( + std::unique_ptr<BluetoothDevice> device, + mojo::PendingReceiver<mojom::SerialPort> receiver, + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher); + + BluetoothSerialPortImpl( + std::unique_ptr<BluetoothDevice> device, + mojo::PendingReceiver<mojom::SerialPort> receiver, + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher); + BluetoothSerialPortImpl(const BluetoothSerialPortImpl&) = delete; + BluetoothSerialPortImpl& operator=(const BluetoothSerialPortImpl&) = delete; + ~BluetoothSerialPortImpl() override; + + private: + // mojom::SerialPort methods: + void Open(mojom::SerialConnectionOptionsPtr options, + mojo::PendingRemote<mojom::SerialPortClient> client, + OpenCallback callback) override; + void StartWriting(mojo::ScopedDataPipeConsumerHandle consumer) override; + void StartReading(mojo::ScopedDataPipeProducerHandle producer) override; + void Flush(mojom::SerialPortFlushMode mode, FlushCallback callback) override; + void Drain(DrainCallback callback) override; + void GetControlSignals(GetControlSignalsCallback callback) override; + void SetControlSignals(mojom::SerialHostControlSignalsPtr signals, + SetControlSignalsCallback callback) override; + void ConfigurePort(mojom::SerialConnectionOptionsPtr options, + ConfigurePortCallback callback) override; + void GetPortInfo(GetPortInfoCallback callback) override; + void Close(CloseCallback callback) override; + + void WriteToSocket(MojoResult result, const mojo::HandleSignalsState& state); + void ReadFromSocketAndWriteOut(MojoResult result, + const mojo::HandleSignalsState& state); + + void ReadMore(); + void WriteMore(); + + void OnSocketConnected(OpenCallback callback, + scoped_refptr<BluetoothSocket> socket); + void OnSocketConnectedError(OpenCallback callback, + const std::string& message); + + void OnBluetoothSocketReceive(base::span<char> pending_write_buffer, + int num_bytes_received, + scoped_refptr<net::IOBuffer> io_buffer); + void OnBluetoothSocketReceiveError( + device::BluetoothSocket::ErrorReason error_reason, + const std::string& error_message); + void OnBluetoothSocketSend(int num_bytes_sent); + void OnBluetoothSocketSendError(const std::string& error_message); + + mojo::Receiver<mojom::SerialPort> receiver_; + mojo::Remote<mojom::SerialPortConnectionWatcher> watcher_; + mojo::Remote<mojom::SerialPortClient> client_; + + // Data pipes for input and output. + mojo::ScopedDataPipeConsumerHandle in_stream_; + mojo::SimpleWatcher in_stream_watcher_; + mojo::ScopedDataPipeProducerHandle out_stream_; + mojo::SimpleWatcher out_stream_watcher_; + + // Holds the callback for a flush or drain until pending operations have been + // completed. + FlushCallback read_flush_callback_; + FlushCallback write_flush_callback_; + DrainCallback drain_callback_; + + scoped_refptr<device::BluetoothSocket> bluetooth_socket_; + std::unique_ptr<BluetoothDevice> bluetooth_device_; + + bool read_pending_ = false; + bool write_pending_ = false; + + mojom::SerialConnectionOptionsPtr options_; + + base::WeakPtrFactory<BluetoothSerialPortImpl> weak_ptr_factory_{this}; +}; + +} // namespace device + +#endif // SERVICES_DEVICE_SERIAL_BLUETOOTH_SERIAL_PORT_IMPL_H_ diff --git a/chromium/services/device/serial/bluetooth_serial_port_impl_unittest.cc b/chromium/services/device/serial/bluetooth_serial_port_impl_unittest.cc new file mode 100644 index 00000000000..0067f97ebd7 --- /dev/null +++ b/chromium/services/device/serial/bluetooth_serial_port_impl_unittest.cc @@ -0,0 +1,353 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/serial/bluetooth_serial_port_impl.h" + +#include "base/command_line.h" +#include "base/test/bind_test_util.h" +#include "base/test/gmock_callback_support.h" +#include "base/test/task_environment.h" +#include "device/bluetooth/bluetooth_adapter_factory.h" +#include "device/bluetooth/bluetooth_socket.h" +#include "device/bluetooth/test/mock_bluetooth_adapter.h" +#include "device/bluetooth/test/mock_bluetooth_device.h" +#include "device/bluetooth/test/mock_bluetooth_socket.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/simple_watcher.h" +#include "net/base/io_buffer.h" +#include "services/device/public/cpp/bluetooth/bluetooth_utils.h" +#include "services/device/public/cpp/serial/serial_switches.h" +#include "services/device/public/mojom/serial.mojom.h" +#include "services/device/serial/buffer.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace device { + +namespace { + +using ::base::test::RunOnceCallback; +using ::testing::_; +using ::testing::Invoke; +using ::testing::WithArgs; + +constexpr char kBuffer[] = "test"; +constexpr char kDeviceAddress[] = "00:00:00:00:00:00"; +constexpr uint32_t kElementNumBytes = 1; +constexpr uint32_t kCapacityNumBytes = 64; + +class BluetoothSerialPortImplTest : public testing::Test { + public: + BluetoothSerialPortImplTest() { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableBluetoothSerialPortProfileInSerialApi); + } + BluetoothSerialPortImplTest(const BluetoothSerialPortImplTest&) = delete; + BluetoothSerialPortImplTest& operator=(const BluetoothSerialPortImplTest&) = + delete; + ~BluetoothSerialPortImplTest() override = default; + + void CreatePort( + mojo::Remote<mojom::SerialPort>* port, + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher>* watcher) { + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher_remote; + *watcher = mojo::MakeSelfOwnedReceiver( + std::make_unique<mojom::SerialPortConnectionWatcher>(), + watcher_remote.InitWithNewPipeAndPassReceiver()); + + scoped_refptr<MockBluetoothAdapter> adapter = + base::MakeRefCounted<MockBluetoothAdapter>(); + device::BluetoothAdapterFactory::SetAdapterForTesting(adapter); + auto mock_device = std::make_unique<MockBluetoothDevice>( + adapter.get(), 0, "Test Device", kDeviceAddress, false, false); + mock_device->AddUUID(GetSerialPortProfileUUID()); + + EXPECT_CALL(*mock_device, + ConnectToService(GetSerialPortProfileUUID(), _, _)) + .WillOnce(RunOnceCallback<1>(mock_socket_)); + + BluetoothSerialPortImpl::Create(std::move(mock_device), + port->BindNewPipeAndPassReceiver(), + std::move(watcher_remote)); + } + + void CreatePortWithSocketError( + mojo::Remote<mojom::SerialPort>* port, + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher>* watcher) { + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher_remote; + *watcher = mojo::MakeSelfOwnedReceiver( + std::make_unique<mojom::SerialPortConnectionWatcher>(), + watcher_remote.InitWithNewPipeAndPassReceiver()); + + scoped_refptr<MockBluetoothAdapter> adapter = + base::MakeRefCounted<MockBluetoothAdapter>(); + device::BluetoothAdapterFactory::SetAdapterForTesting(adapter); + auto mock_device = std::make_unique<MockBluetoothDevice>( + adapter.get(), 0, "Test Device", kDeviceAddress, false, false); + mock_device->AddUUID(GetSerialPortProfileUUID()); + + EXPECT_CALL(*mock_device, + ConnectToService(GetSerialPortProfileUUID(), _, _)) + .WillOnce(RunOnceCallback<2>("Error")); + + BluetoothSerialPortImpl::Create(std::move(mock_device), + port->BindNewPipeAndPassReceiver(), + std::move(watcher_remote)); + } + + void CreateDataPipe(mojo::ScopedDataPipeProducerHandle* producer, + mojo::ScopedDataPipeConsumerHandle* consumer) { + MojoCreateDataPipeOptions options; + options.struct_size = sizeof(options); + options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; + options.element_num_bytes = kElementNumBytes; + options.capacity_num_bytes = kCapacityNumBytes; + + MojoResult result = mojo::CreateDataPipe(&options, producer, consumer); + DCHECK_EQ(result, MOJO_RESULT_OK); + } + + MockBluetoothSocket& mock_socket() { return *mock_socket_; } + + private: + scoped_refptr<MockBluetoothSocket> mock_socket_ = + base::MakeRefCounted<MockBluetoothSocket>(); + + base::test::SingleThreadTaskEnvironment task_environment_; +}; + +class FakeSerialPortClient : public mojom::SerialPortClient { + public: + FakeSerialPortClient() = default; + FakeSerialPortClient(FakeSerialPortClient&) = delete; + FakeSerialPortClient& operator=(FakeSerialPortClient&) = delete; + ~FakeSerialPortClient() override = default; + + void Bind(mojo::PendingReceiver<device::mojom::SerialPortClient> receiver) { + receiver_.Bind(std::move(receiver)); + } + + // mojom::SerialPortClient + void OnReadError(mojom::SerialReceiveError error) override {} + void OnSendError(mojom::SerialSendError error) override {} + + private: + mojo::Receiver<mojom::SerialPortClient> receiver_{this}; +}; + +} // namespace + +TEST_F(BluetoothSerialPortImplTest, NullSocketTest) { + mojo::Remote<mojom::SerialPort> serial_port; + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePortWithSocketError(&serial_port, &watcher); + + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + CreateDataPipe(&producer, &consumer); + + auto options = mojom::SerialConnectionOptions::New(); + mojo::PendingRemote<mojom::SerialPortClient> client; + FakeSerialPortClient serial_client; + serial_client.Bind(client.InitWithNewPipeAndPassReceiver()); + base::RunLoop loop; + serial_port->Open(std::move(options), std::move(client), + base::BindLambdaForTesting([&loop](bool success) { + EXPECT_FALSE(success); + loop.Quit(); + })); + loop.Run(); + + EXPECT_CALL(mock_socket(), Receive(_, _, _)).Times(0); + EXPECT_CALL(mock_socket(), Close()).Times(0); + + serial_port->StartReading(std::move(producer)); + + base::RunLoop disconnect_loop; + watcher->set_connection_error_handler(disconnect_loop.QuitClosure()); + + serial_port.reset(); + disconnect_loop.Run(); +} + +TEST_F(BluetoothSerialPortImplTest, StartWritingTest) { + mojo::Remote<mojom::SerialPort> serial_port; + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); + + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + CreateDataPipe(&producer, &consumer); + + auto options = mojom::SerialConnectionOptions::New(); + mojo::PendingRemote<mojom::SerialPortClient> client; + FakeSerialPortClient serial_client; + serial_client.Bind(client.InitWithNewPipeAndPassReceiver()); + base::RunLoop loop; + serial_port->Open(std::move(options), std::move(client), + base::BindLambdaForTesting([&loop](bool success) { + EXPECT_TRUE(success); + loop.Quit(); + })); + loop.Run(); + + uint32_t bytes_read = std::char_traits<char>::length(kBuffer); + auto write_buffer = base::MakeRefCounted<net::StringIOBuffer>(kBuffer); + + MojoResult result = + producer->WriteData(&kBuffer, &bytes_read, MOJO_WRITE_DATA_FLAG_NONE); + EXPECT_EQ(result, MOJO_RESULT_OK); + + EXPECT_CALL(mock_socket(), Send) + .WillOnce(WithArgs<0, 1, 2>(Invoke( + [&](scoped_refptr<net::IOBuffer> buf, int buffer_size, + MockBluetoothSocket::SendCompletionCallback success_callback) { + ASSERT_EQ(buffer_size, int{bytes_read}); + // EXPECT_EQ only does a shallow comparison, so it's necessary to + // iterate through both objects and compare each character. + for (int i = 0; i < buffer_size; i++) { + EXPECT_EQ(buf->data()[i], kBuffer[i]) + << "buffer comparison failed at index " << i; + } + std::move(success_callback).Run(buffer_size); + }))); + + EXPECT_CALL(mock_socket(), Close()); + + serial_port->StartWriting(std::move(consumer)); + + EXPECT_EQ(write_buffer->size(), int{bytes_read}); + + base::RunLoop disconnect_loop; + watcher->set_connection_error_handler(disconnect_loop.QuitClosure()); + + serial_port.reset(); + disconnect_loop.Run(); +} + +TEST_F(BluetoothSerialPortImplTest, StartReadingTest) { + mojo::Remote<mojom::SerialPort> serial_port; + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); + + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + CreateDataPipe(&producer, &consumer); + + auto options = mojom::SerialConnectionOptions::New(); + mojo::PendingRemote<mojom::SerialPortClient> client; + FakeSerialPortClient serial_client; + serial_client.Bind(client.InitWithNewPipeAndPassReceiver()); + base::RunLoop loop; + serial_port->Open(std::move(options), std::move(client), + base::BindLambdaForTesting([&loop](bool success) { + EXPECT_TRUE(success); + loop.Quit(); + })); + loop.Run(); + + uint32_t bytes_read = std::char_traits<char>::length(kBuffer); + auto write_buffer = base::MakeRefCounted<net::StringIOBuffer>(kBuffer); + + MojoResult result = + producer->WriteData(&kBuffer, &bytes_read, MOJO_WRITE_DATA_FLAG_NONE); + EXPECT_EQ(result, MOJO_RESULT_OK); + + EXPECT_CALL(mock_socket(), Receive(_, _, _)) + .WillOnce(RunOnceCallback<1>(write_buffer->size(), write_buffer)) + .WillOnce(RunOnceCallback<2>(BluetoothSocket::kSystemError, "Error")); + EXPECT_CALL(mock_socket(), Close()); + + serial_port->StartReading(std::move(producer)); + + ASSERT_EQ(write_buffer->size(), int{bytes_read}); + int size = write_buffer->size(); + // EXPECT_EQ only does a shallow comparison, so it's necessary to iterate + // through both objects and compare each character. + for (int i = 0; i < size; i++) { + EXPECT_EQ(write_buffer->data()[i], kBuffer[i]) + << "buffer comparison failed at index " << i; + } + + base::RunLoop disconnect_loop; + watcher->set_connection_error_handler(disconnect_loop.QuitClosure()); + + serial_port.reset(); + disconnect_loop.Run(); +} + +TEST_F(BluetoothSerialPortImplTest, Drain) { + mojo::Remote<mojom::SerialPort> serial_port; + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); + + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + CreateDataPipe(&producer, &consumer); + + auto options = mojom::SerialConnectionOptions::New(); + mojo::PendingRemote<mojom::SerialPortClient> client; + FakeSerialPortClient serial_client; + serial_client.Bind(client.InitWithNewPipeAndPassReceiver()); + base::RunLoop loop; + serial_port->Open(std::move(options), std::move(client), + base::BindLambdaForTesting([&loop](bool success) { + EXPECT_TRUE(success); + loop.Quit(); + })); + loop.Run(); + + serial_port->StartWriting(std::move(consumer)); + + producer.reset(); + + base::RunLoop drain_loop; + serial_port->Drain(drain_loop.QuitClosure()); + drain_loop.Run(); + + base::RunLoop disconnect_loop; + watcher->set_connection_error_handler(disconnect_loop.QuitClosure()); + + serial_port.reset(); + disconnect_loop.Run(); +} + +TEST_F(BluetoothSerialPortImplTest, Close) { + mojo::Remote<mojom::SerialPort> serial_port; + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); + + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + CreateDataPipe(&producer, &consumer); + + auto options = mojom::SerialConnectionOptions::New(); + mojo::PendingRemote<mojom::SerialPortClient> client; + FakeSerialPortClient serial_client; + serial_client.Bind(client.InitWithNewPipeAndPassReceiver()); + base::RunLoop loop; + serial_port->Open( + std::move(options), std::move(client), + base::BindOnce([](base::RunLoop* loop, bool success) { loop->Quit(); }, + &loop)); + loop.Run(); + + EXPECT_CALL(mock_socket(), Close()); + + base::RunLoop close_loop; + serial_port->Close(close_loop.QuitClosure()); + close_loop.Run(); + + base::RunLoop disconnect_loop; + watcher->set_connection_error_handler( + base::BindLambdaForTesting([&]() { disconnect_loop.Quit(); })); + + serial_port.reset(); + disconnect_loop.Run(); +} + +} // namespace device diff --git a/chromium/services/device/serial/serial_device_enumerator.cc b/chromium/services/device/serial/serial_device_enumerator.cc index 057efc7654a..9e0e851b3ca 100644 --- a/chromium/services/device/serial/serial_device_enumerator.cc +++ b/chromium/services/device/serial/serial_device_enumerator.cc @@ -9,9 +9,9 @@ #include "base/unguessable_token.h" #include "build/build_config.h" -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) #include "services/device/serial/serial_device_enumerator_linux.h" -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) #include "services/device/serial/serial_device_enumerator_mac.h" #elif defined(OS_WIN) #include "services/device/serial/serial_device_enumerator_win.h" @@ -22,9 +22,9 @@ namespace device { // static std::unique_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create( scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { -#if defined(OS_LINUX) - return std::make_unique<SerialDeviceEnumeratorLinux>(); -#elif defined(OS_MACOSX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) + return SerialDeviceEnumeratorLinux::Create(); +#elif defined(OS_MAC) return std::make_unique<SerialDeviceEnumeratorMac>(); #elif defined(OS_WIN) return std::make_unique<SerialDeviceEnumeratorWin>(std::move(ui_task_runner)); @@ -66,7 +66,7 @@ base::Optional<base::FilePath> SerialDeviceEnumerator::GetPathFromToken( if (it == ports_.end()) return base::nullopt; -#if defined(OS_MACOSX) +#if defined(OS_MAC) if (use_alternate_path) return it->second->alternate_path; #endif diff --git a/chromium/services/device/serial/serial_device_enumerator_linux.cc b/chromium/services/device/serial/serial_device_enumerator_linux.cc index a90aa6afded..e172a70fa56 100644 --- a/chromium/services/device/serial/serial_device_enumerator_linux.cc +++ b/chromium/services/device/serial/serial_device_enumerator_linux.cc @@ -33,10 +33,9 @@ struct SerialDriverInfo { int minor_end; // Inclusive. }; -std::vector<SerialDriverInfo> ReadSerialDriverInfo() { +std::vector<SerialDriverInfo> ReadSerialDriverInfo(const base::FilePath& path) { std::string tty_drivers; - if (!base::ReadFileToString(base::FilePath("/proc/tty/drivers"), - &tty_drivers)) { + if (!base::ReadFileToString(path, &tty_drivers)) { return {}; } @@ -84,12 +83,22 @@ std::vector<SerialDriverInfo> ReadSerialDriverInfo() { } // namespace -SerialDeviceEnumeratorLinux::SerialDeviceEnumeratorLinux() { +// static +std::unique_ptr<SerialDeviceEnumeratorLinux> +SerialDeviceEnumeratorLinux::Create() { + return std::make_unique<SerialDeviceEnumeratorLinux>( + base::FilePath("/proc/tty/drivers")); +} + +SerialDeviceEnumeratorLinux::SerialDeviceEnumeratorLinux( + const base::FilePath& tty_driver_info_path) + : tty_driver_info_path_(tty_driver_info_path) { DETACH_FROM_SEQUENCE(sequence_checker_); watcher_ = UdevWatcher::StartWatching( this, {UdevWatcher::Filter(kSubsystemTty, "")}); - watcher_->EnumerateExistingDevices(); + if (watcher_) + watcher_->EnumerateExistingDevices(); } SerialDeviceEnumeratorLinux::~SerialDeviceEnumeratorLinux() { @@ -121,7 +130,7 @@ void SerialDeviceEnumeratorLinux::OnDeviceAdded(ScopedUdevDevicePtr device) { return; } - for (const auto& driver : ReadSerialDriverInfo()) { + for (const auto& driver : ReadSerialDriverInfo(tty_driver_info_path_)) { if (major == driver.major && minor >= driver.minor_start && minor <= driver.minor_end) { CreatePort(std::move(device), syspath); @@ -164,7 +173,7 @@ void SerialDeviceEnumeratorLinux::CreatePort(ScopedUdevDevicePtr device, const char* vendor_id = udev_device_get_property_value(device.get(), "ID_VENDOR_ID"); const char* product_id = - udev_device_get_property_value(device.get(), "ID_PRODUCT_ID"); + udev_device_get_property_value(device.get(), "ID_MODEL_ID"); const char* product_name_enc = udev_device_get_property_value(device.get(), "ID_MODEL_ENC"); const char* serial_number = diff --git a/chromium/services/device/serial/serial_device_enumerator_linux.h b/chromium/services/device/serial/serial_device_enumerator_linux.h index 16ea3816656..17c02843f57 100644 --- a/chromium/services/device/serial/serial_device_enumerator_linux.h +++ b/chromium/services/device/serial/serial_device_enumerator_linux.h @@ -19,7 +19,10 @@ namespace device { class SerialDeviceEnumeratorLinux : public SerialDeviceEnumerator, public UdevWatcher::Observer { public: - SerialDeviceEnumeratorLinux(); + static std::unique_ptr<SerialDeviceEnumeratorLinux> Create(); + + explicit SerialDeviceEnumeratorLinux( + const base::FilePath& tty_driver_info_path); ~SerialDeviceEnumeratorLinux() override; // UdevWatcher::Observer @@ -31,6 +34,7 @@ class SerialDeviceEnumeratorLinux : public SerialDeviceEnumerator, void CreatePort(ScopedUdevDevicePtr device, const std::string& syspath); std::unique_ptr<UdevWatcher> watcher_; + const base::FilePath tty_driver_info_path_; std::map<std::string, base::UnguessableToken> paths_; DISALLOW_COPY_AND_ASSIGN(SerialDeviceEnumeratorLinux); diff --git a/chromium/services/device/serial/serial_device_enumerator_linux_unittest.cc b/chromium/services/device/serial/serial_device_enumerator_linux_unittest.cc new file mode 100644 index 00000000000..94076f12de5 --- /dev/null +++ b/chromium/services/device/serial/serial_device_enumerator_linux_unittest.cc @@ -0,0 +1,79 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/serial/serial_device_enumerator_linux.h" + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/test/task_environment.h" +#include "device/udev_linux/fake_udev_loader.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace device { + +constexpr char kSerialDriverInfo[] = + R"(/dev/tty /dev/tty 5 0 system:/dev/tty +/dev/console /dev/console 5 1 system:console +/dev/ptmx /dev/ptmx 5 2 system +/dev/vc/0 /dev/vc/0 4 0 system:vtmaster +acm /dev/ttyACM 166 0-255 serial +ttyAMA /dev/ttyAMA 204 64-77 serial +ttyprintk /dev/ttyprintk 5 3 console +max310x /dev/ttyMAX 204 209-224 serial +serial /dev/ttyS 4 64 serial +pty_slave /dev/pts 136 0-1048575 pty:slave +pty_master /dev/ptm 128 0-1048575 pty:master +pty_slave /dev/ttyp 3 0-255 pty:slave +pty_master /dev/pty 2 0-255 pty:master +unknown /dev/tty 4 1-63 console)"; + +class SerialDeviceEnumeratorLinuxTest : public testing::Test { + public: + void SetUp() override { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + drivers_file_ = temp_dir_.GetPath().Append("drivers"); + ASSERT_TRUE(base::WriteFile(drivers_file_, kSerialDriverInfo)); + } + + std::unique_ptr<SerialDeviceEnumeratorLinux> CreateEnumerator() { + return std::make_unique<SerialDeviceEnumeratorLinux>(drivers_file_); + } + + private: + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::MainThreadType::IO}; + base::ScopedTempDir temp_dir_; + base::FilePath drivers_file_; +}; + +TEST_F(SerialDeviceEnumeratorLinuxTest, Enumerate) { + testing::FakeUdevLoader fake_udev; + fake_udev.AddFakeDevice(/*name=*/"ttyACM0", + /*syspath=*/"/sys/class/tty/ttyACM0", + /*subsystem=*/"tty", /*sysattrs=*/{}, + /*properties=*/ + { + {"DEVNAME", "/dev/ttyACM0"}, + {"MAJOR", "166"}, + {"MINOR", "0"}, + {"ID_VENDOR_ID", "2341"}, + {"ID_MODEL_ID", "0043"}, + {"ID_MODEL_ENC", "Arduino\\x20Uno"}, + {"ID_SERIAL_SHORT", "000001"}, + }); + + std::unique_ptr<SerialDeviceEnumeratorLinux> enumerator = CreateEnumerator(); + std::vector<mojom::SerialPortInfoPtr> devices = enumerator->GetDevices(); + ASSERT_EQ(devices.size(), 1u); + EXPECT_EQ(devices[0]->persistent_id, "2341-0043-000001"); + EXPECT_EQ(devices[0]->path, base::FilePath("/dev/ttyACM0")); + EXPECT_TRUE(devices[0]->has_vendor_id); + EXPECT_EQ(devices[0]->vendor_id, 0x2341); + EXPECT_TRUE(devices[0]->has_product_id); + EXPECT_EQ(devices[0]->product_id, 0x0043); + EXPECT_EQ(devices[0]->display_name, "Arduino Uno"); +} + +} // namespace device diff --git a/chromium/services/device/serial/serial_device_enumerator_win.cc b/chromium/services/device/serial/serial_device_enumerator_win.cc index 931173b8f55..5adf77753d6 100644 --- a/chromium/services/device/serial/serial_device_enumerator_win.cc +++ b/chromium/services/device/serial/serial_device_enumerator_win.cc @@ -54,7 +54,7 @@ base::Optional<std::string> GetProperty(HDEVINFO dev_info, return base::nullopt; } - base::string16 buffer; + std::wstring buffer; if (!SetupDiGetDeviceProperty( dev_info, dev_info_data, &property, &property_type, reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)), @@ -62,7 +62,7 @@ base::Optional<std::string> GetProperty(HDEVINFO dev_info, return base::nullopt; } - return base::UTF16ToUTF8(buffer); + return base::WideToUTF8(buffer); } base::FilePath FixUpPortName(base::StringPiece port_name) { @@ -74,6 +74,17 @@ base::FilePath FixUpPortName(base::StringPiece port_name) { return base::FilePath::FromUTF8Unsafe(port_name); } +// Searches for the COM port in the device's friendly name and returns the +// appropriate device path or nullopt if the input did not contain a valid +// name. +base::Optional<base::FilePath> GetPath(const std::string& friendly_name) { + std::string com_port; + if (!RE2::PartialMatch(friendly_name, ".* \\((COM[0-9]+)\\)", &com_port)) + return base::nullopt; + + return FixUpPortName(com_port); +} + // Searches for the display name in the device's friendly name, assigns its // value to display_name, and returns whether the operation was successful. bool GetDisplayName(const std::string friendly_name, @@ -124,7 +135,7 @@ class SerialDeviceEnumeratorWin::UiThreadHelper } void OnDeviceAdded(const GUID& class_guid, - const base::string16& device_path) override { + const std::wstring& device_path) override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); task_runner_->PostTask( FROM_HERE, base::BindOnce(&SerialDeviceEnumeratorWin::OnPathAdded, @@ -132,7 +143,7 @@ class SerialDeviceEnumeratorWin::UiThreadHelper } void OnDeviceRemoved(const GUID& class_guid, - const base::string16& device_path) override { + const std::wstring& device_path) override { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); task_runner_->PostTask( FROM_HERE, base::BindOnce(&SerialDeviceEnumeratorWin::OnPathRemoved, @@ -166,17 +177,7 @@ SerialDeviceEnumeratorWin::SerialDeviceEnumeratorWin( SerialDeviceEnumeratorWin::~SerialDeviceEnumeratorWin() = default; -// static -base::Optional<base::FilePath> SerialDeviceEnumeratorWin::GetPath( - const std::string& friendly_name) { - std::string com_port; - if (!RE2::PartialMatch(friendly_name, ".* \\((COM[0-9]+)\\)", &com_port)) - return base::nullopt; - - return FixUpPortName(com_port); -} - -void SerialDeviceEnumeratorWin::OnPathAdded(const base::string16& device_path) { +void SerialDeviceEnumeratorWin::OnPathAdded(const std::wstring& device_path) { base::win::ScopedDevInfo dev_info( SetupDiCreateDeviceInfoList(nullptr, nullptr)); if (!dev_info.is_valid()) @@ -195,8 +196,7 @@ void SerialDeviceEnumeratorWin::OnPathAdded(const base::string16& device_path) { EnumeratePort(dev_info.get(), &dev_info_data); } -void SerialDeviceEnumeratorWin::OnPathRemoved( - const base::string16& device_path) { +void SerialDeviceEnumeratorWin::OnPathRemoved(const std::wstring& device_path) { base::win::ScopedDevInfo dev_info( SetupDiCreateDeviceInfoList(nullptr, nullptr)); if (!dev_info.is_valid()) diff --git a/chromium/services/device/serial/serial_device_enumerator_win.h b/chromium/services/device/serial/serial_device_enumerator_win.h index 553a73df2a1..a41d9b720ff 100644 --- a/chromium/services/device/serial/serial_device_enumerator_win.h +++ b/chromium/services/device/serial/serial_device_enumerator_win.h @@ -23,14 +23,8 @@ class SerialDeviceEnumeratorWin : public SerialDeviceEnumerator { scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); ~SerialDeviceEnumeratorWin() override; - // Searches for the COM port in the device's friendly name and returns the - // appropriate device path or nullopt if the input did not contain a valid - // name. - static base::Optional<base::FilePath> GetPath( - const std::string& friendly_name); - - void OnPathAdded(const base::string16& device_path); - void OnPathRemoved(const base::string16& device_path); + void OnPathAdded(const std::wstring& device_path); + void OnPathRemoved(const std::wstring& device_path); private: class UiThreadHelper; diff --git a/chromium/services/device/serial/serial_io_handler.cc b/chromium/services/device/serial/serial_io_handler.cc index a68b67c5fb3..54e25511625 100644 --- a/chromium/services/device/serial/serial_io_handler.cc +++ b/chromium/services/device/serial/serial_io_handler.cc @@ -174,6 +174,8 @@ void SerialIoHandler::Close(base::OnceClosure callback) { {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, base::BindOnce(&SerialIoHandler::DoClose, std::move(file_)), std::move(callback)); + } else { + std::move(callback).Run(); } } diff --git a/chromium/services/device/serial/serial_io_handler.h b/chromium/services/device/serial/serial_io_handler.h index b70906f7bca..cd49076f551 100644 --- a/chromium/services/device/serial/serial_io_handler.h +++ b/chromium/services/device/serial/serial_io_handler.h @@ -80,7 +80,10 @@ class SerialIoHandler : public base::RefCountedThreadSafe<SerialIoHandler> { void CancelWrite(mojom::SerialSendError reason); // Flushes input and output buffers. - virtual bool Flush() const = 0; + virtual void Flush(mojom::SerialPortFlushMode mode) const = 0; + + // Drains output buffers. + virtual void Drain() = 0; // Reads current control signals (DCD, CTS, etc.) into an existing // DeviceControlSignals structure. Returns |true| iff the signals were diff --git a/chromium/services/device/serial/serial_io_handler_posix.cc b/chromium/services/device/serial/serial_io_handler_posix.cc index 7f2a2c421b3..ad3ae505e15 100644 --- a/chromium/services/device/serial/serial_io_handler_posix.cc +++ b/chromium/services/device/serial/serial_io_handler_posix.cc @@ -15,7 +15,7 @@ #include "base/posix/eintr_wrapper.h" #include "build/build_config.h" -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) #include <asm-generic/ioctls.h> #include <linux/serial.h> @@ -34,9 +34,9 @@ struct termios2 { }; } -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) -#if defined(OS_MACOSX) +#if defined(OS_MAC) #include <IOKit/serial/ioss.h> #endif @@ -66,7 +66,7 @@ bool BitrateToSpeedConstant(int bitrate, speed_t* speed) { BITRATE_TO_SPEED_CASE(9600) BITRATE_TO_SPEED_CASE(19200) BITRATE_TO_SPEED_CASE(38400) -#if !defined(OS_MACOSX) +#if !defined(OS_MAC) BITRATE_TO_SPEED_CASE(57600) BITRATE_TO_SPEED_CASE(115200) BITRATE_TO_SPEED_CASE(230400) @@ -80,7 +80,7 @@ bool BitrateToSpeedConstant(int bitrate, speed_t* speed) { #undef BITRATE_TO_SPEED_CASE } -#if !defined(OS_LINUX) +#if !defined(OS_LINUX) && !defined(OS_CHROMEOS) // Convert a known nominal speed into an integral bitrate. Returns |true| // if the conversion was successful and |false| otherwise. bool SpeedConstantToBitrate(speed_t speed, int* bitrate) { @@ -164,7 +164,7 @@ void SerialIoHandlerPosix::CancelWriteImpl() { } bool SerialIoHandlerPosix::ConfigurePortImpl() { -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) struct termios2 config; if (ioctl(file().GetPlatformFile(), TCGETS2, &config) < 0) { #else @@ -187,11 +187,11 @@ bool SerialIoHandlerPosix::ConfigurePortImpl() { DCHECK(options().bitrate); speed_t bitrate_opt = B0; -#if defined(OS_MACOSX) +#if defined(OS_MAC) bool need_iossiospeed = false; #endif if (BitrateToSpeedConstant(options().bitrate, &bitrate_opt)) { -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) config.c_cflag &= ~CBAUD; config.c_cflag |= bitrate_opt; #else @@ -200,11 +200,11 @@ bool SerialIoHandlerPosix::ConfigurePortImpl() { #endif } else { // Attempt to set a custom speed. -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) config.c_cflag &= ~CBAUD; config.c_cflag |= CBAUDEX; config.c_ispeed = config.c_ospeed = options().bitrate; -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) // cfsetispeed and cfsetospeed sometimes work for custom baud rates on OS // X but the IOSSIOSPEED ioctl is more reliable but has to be done after // the rest of the port parameters are set or else it will be overwritten. @@ -272,7 +272,7 @@ bool SerialIoHandlerPosix::ConfigurePortImpl() { config.c_cflag &= ~CRTSCTS; } -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) if (ioctl(file().GetPlatformFile(), TCSETS2, &config) < 0) { #else if (tcsetattr(file().GetPlatformFile(), TCSANOW, &config) != 0) { @@ -281,7 +281,7 @@ bool SerialIoHandlerPosix::ConfigurePortImpl() { return false; } -#if defined(OS_MACOSX) +#if defined(OS_MAC) if (need_iossiospeed) { speed_t bitrate = options().bitrate; if (ioctl(file().GetPlatformFile(), IOSSIOSPEED, &bitrate) == -1) { @@ -317,7 +317,6 @@ SerialIoHandlerPosix::~SerialIoHandlerPosix() = default; void SerialIoHandlerPosix::AttemptRead(bool within_read) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (pending_read_buffer()) { int bytes_read = HANDLE_EINTR(read(file().GetPlatformFile(), pending_read_buffer(), @@ -329,12 +328,15 @@ void SerialIoHandlerPosix::AttemptRead(bool within_read) { } else if (errno == ENXIO) { RunReadCompleted(within_read, 0, mojom::SerialReceiveError::DEVICE_LOST); + StopWatchingFileRead(); } else { + VPLOG(1) << "Read failed"; RunReadCompleted(within_read, 0, mojom::SerialReceiveError::SYSTEM_ERROR); } } else if (bytes_read == 0) { RunReadCompleted(within_read, 0, mojom::SerialReceiveError::DEVICE_LOST); + StopWatchingFileRead(); } else { bool break_detected = false; bool parity_error_detected = false; @@ -376,13 +378,18 @@ void SerialIoHandlerPosix::RunReadCompleted(bool within_read, void SerialIoHandlerPosix::OnFileCanWriteWithoutBlocking() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (pending_write_buffer()) { int bytes_written = HANDLE_EINTR(write(file().GetPlatformFile(), pending_write_buffer(), pending_write_buffer_len())); if (bytes_written < 0) { - WriteCompleted(0, mojom::SerialSendError::SYSTEM_ERROR); + if (errno == ENXIO) { + WriteCompleted(0, mojom::SerialSendError::DISCONNECTED); + StopWatchingFileWrite(); + } else { + VPLOG(1) << "Write failed"; + WriteCompleted(0, mojom::SerialSendError::SYSTEM_ERROR); + } } else { WriteCompleted(bytes_written, mojom::SerialSendError::NONE); } @@ -436,12 +443,27 @@ void SerialIoHandlerPosix::StopWatchingFileWrite() { } } -bool SerialIoHandlerPosix::Flush() const { - if (tcflush(file().GetPlatformFile(), TCIOFLUSH) != 0) { - VPLOG(1) << "Failed to flush port"; - return false; +void SerialIoHandlerPosix::Flush(mojom::SerialPortFlushMode mode) const { + int queue_selector; + switch (mode) { + case mojom::SerialPortFlushMode::kReceiveAndTransmit: + queue_selector = TCIOFLUSH; + break; + case mojom::SerialPortFlushMode::kReceive: + queue_selector = TCIFLUSH; + break; + case mojom::SerialPortFlushMode::kTransmit: + queue_selector = TCOFLUSH; + break; } - return true; + + if (tcflush(file().GetPlatformFile(), queue_selector) != 0) + VPLOG(1) << "Failed to flush port"; +} + +void SerialIoHandlerPosix::Drain() { + if (tcdrain(file().GetPlatformFile()) != 0) + VPLOG(1) << "Failed to drain port"; } mojom::SerialPortControlSignalsPtr SerialIoHandlerPosix::GetControlSignals() @@ -510,7 +532,7 @@ bool SerialIoHandlerPosix::SetControlSignals( } mojom::SerialConnectionInfoPtr SerialIoHandlerPosix::GetPortInfo() const { -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) struct termios2 config; if (ioctl(file().GetPlatformFile(), TCGETS2, &config) < 0) { #else @@ -522,7 +544,7 @@ mojom::SerialConnectionInfoPtr SerialIoHandlerPosix::GetPortInfo() const { } auto info = mojom::SerialConnectionInfo::New(); -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) // Linux forces c_ospeed to contain the correct value, which is nice. info->bitrate = config.c_ospeed; #else diff --git a/chromium/services/device/serial/serial_io_handler_posix.h b/chromium/services/device/serial/serial_io_handler_posix.h index 3d69c6d04c4..51311d6abce 100644 --- a/chromium/services/device/serial/serial_io_handler_posix.h +++ b/chromium/services/device/serial/serial_io_handler_posix.h @@ -31,7 +31,8 @@ class SerialIoHandlerPosix : public SerialIoHandler { bool ConfigurePortImpl() override; bool PostOpen() override; void PreClose() override; - bool Flush() const override; + void Flush(mojom::SerialPortFlushMode mode) const override; + void Drain() override; mojom::SerialPortControlSignalsPtr GetControlSignals() const override; bool SetControlSignals( const mojom::SerialHostControlSignals& control_signals) override; diff --git a/chromium/services/device/serial/serial_io_handler_win.cc b/chromium/services/device/serial/serial_io_handler_win.cc index 12d91e68ea9..ca90b1caed1 100644 --- a/chromium/services/device/serial/serial_io_handler_win.cc +++ b/chromium/services/device/serial/serial_io_handler_win.cc @@ -4,21 +4,13 @@ #include "services/device/serial/serial_io_handler_win.h" -#define INITGUID -#include <devpkey.h> -#include <setupapi.h> #include <windows.h> #include <utility> #include "base/bind.h" -#include "base/macros.h" -#include "base/message_loop/message_loop_current.h" -#include "base/scoped_observer.h" #include "base/sequence_checker.h" -#include "device/base/device_info_query_win.h" -#include "device/base/device_monitor_win.h" -#include "services/device/serial/serial_device_enumerator_win.h" +#include "base/task/current_thread.h" namespace device { @@ -153,108 +145,18 @@ scoped_refptr<SerialIoHandler> SerialIoHandler::Create( return new SerialIoHandlerWin(port, std::move(ui_thread_task_runner)); } -class SerialIoHandlerWin::UiThreadHelper final - : public DeviceMonitorWin::Observer { - public: - UiThreadHelper( - base::WeakPtr<SerialIoHandlerWin> io_handler, - scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner) - : device_observer_(this), - io_handler_(io_handler), - io_thread_task_runner_(io_thread_task_runner) {} - - ~UiThreadHelper() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); } - - static void Start(UiThreadHelper* self) { - DETACH_FROM_THREAD(self->thread_checker_); - DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces(); - if (device_monitor) - self->device_observer_.Add(device_monitor); - } - - private: - // DeviceMonitorWin::Observer - void OnDeviceRemoved(const GUID& class_guid, - const base::string16& device_path) override { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - io_thread_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&SerialIoHandlerWin::OnDeviceRemoved, - io_handler_, device_path)); - } - - THREAD_CHECKER(thread_checker_); - ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_; - - // This weak pointer is only valid when checked on this task runner. - base::WeakPtr<SerialIoHandlerWin> io_handler_; - scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner_; - - DISALLOW_COPY_AND_ASSIGN(UiThreadHelper); -}; - -void SerialIoHandlerWin::OnDeviceRemoved(const base::string16& device_path) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - DeviceInfoQueryWin device_info_query; - if (!device_info_query.device_info_list_valid()) { - DVPLOG(1) << "Failed to create a device information set"; - return; - } - - // This will add the device so we can query driver info. - if (!device_info_query.AddDevice(device_path)) { - DVPLOG(1) << "Failed to get device interface data for " << device_path; - return; - } - - if (!device_info_query.GetDeviceInfo()) { - DVPLOG(1) << "Failed to get device info for " << device_path; - return; - } - - std::string friendly_name; - if (!device_info_query.GetDeviceStringProperty(DEVPKEY_Device_FriendlyName, - &friendly_name)) { - DVPLOG(1) << "Failed to get device service property"; - return; - } - - base::Optional<base::FilePath> path = - SerialDeviceEnumeratorWin::GetPath(friendly_name); - if (!path) { - DVPLOG(1) << "Failed to get device path from \"" << friendly_name << "\"."; - return; - } - - if (port() == *path) - CancelRead(mojom::SerialReceiveError::DEVICE_LOST); -} - bool SerialIoHandlerWin::PostOpen() { - DCHECK(!comm_context_); DCHECK(!read_context_); DCHECK(!write_context_); - base::MessageLoopCurrentForIO::Get()->RegisterIOHandler( - file().GetPlatformFile(), this); + base::CurrentIOThread::Get()->RegisterIOHandler(file().GetPlatformFile(), + this); - comm_context_.reset(new base::MessagePumpForIO::IOContext()); read_context_.reset(new base::MessagePumpForIO::IOContext()); write_context_.reset(new base::MessagePumpForIO::IOContext()); - scoped_refptr<base::SingleThreadTaskRunner> io_thread_task_runner = - base::ThreadTaskRunnerHandle::Get(); - helper_ = - new UiThreadHelper(weak_factory_.GetWeakPtr(), io_thread_task_runner); - ui_thread_task_runner()->PostTask( - FROM_HERE, base::BindOnce(&UiThreadHelper::Start, helper_)); - - // A ReadIntervalTimeout of MAXDWORD will cause async reads to complete - // immediately with any data that's available, even if there is none. - // This is OK because we never issue a read request until WaitCommEvent - // signals that data is available. COMMTIMEOUTS timeouts = {0}; - timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadIntervalTimeout = 1; if (!::SetCommTimeouts(file().GetPlatformFile(), &timeouts)) { VPLOG(1) << "Failed to set serial timeouts"; return false; @@ -272,18 +174,16 @@ void SerialIoHandlerWin::ReadImpl() { return; } - if (!SetCommMask(file().GetPlatformFile(), EV_RXCHAR)) { - VPLOG(1) << "Failed to set serial event flags"; - } + ClearPendingError(); + if (!IsReadPending()) + return; - event_mask_ = 0; - BOOL ok = ::WaitCommEvent(file().GetPlatformFile(), &event_mask_, - &comm_context_->overlapped); - if (!ok && GetLastError() != ERROR_IO_PENDING) { - VPLOG(1) << "Failed to receive serial event"; - QueueReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR); + if (!ReadFile(file().GetPlatformFile(), pending_read_buffer(), + pending_read_buffer_len(), nullptr, + &read_context_->overlapped) && + GetLastError() != ERROR_IO_PENDING) { + OnIOCompleted(read_context_.get(), 0, GetLastError()); } - is_comm_pending_ = true; } void SerialIoHandlerWin::WriteImpl() { @@ -295,25 +195,27 @@ void SerialIoHandlerWin::WriteImpl() { return; } - BOOL ok = ::WriteFile(file().GetPlatformFile(), pending_write_buffer(), - pending_write_buffer_len(), NULL, - &write_context_->overlapped); - if (!ok && GetLastError() != ERROR_IO_PENDING) { - VPLOG(1) << "Write failed"; - QueueWriteCompleted(0, mojom::SerialSendError::SYSTEM_ERROR); + if (!WriteFile(file().GetPlatformFile(), pending_write_buffer(), + pending_write_buffer_len(), nullptr, + &write_context_->overlapped) && + GetLastError() != ERROR_IO_PENDING) { + OnIOCompleted(write_context_.get(), 0, GetLastError()); } } void SerialIoHandlerWin::CancelReadImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(file().IsValid()); - ::CancelIo(file().GetPlatformFile()); + + if (!PurgeComm(file().GetPlatformFile(), PURGE_RXABORT)) + VPLOG(1) << "RX abort failed"; } void SerialIoHandlerWin::CancelWriteImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(file().IsValid()); - ::CancelIo(file().GetPlatformFile()); + if (!PurgeComm(file().GetPlatformFile(), PURGE_TXABORT)) + VPLOG(1) << "TX abort failed"; } bool SerialIoHandlerWin::ConfigurePortImpl() { @@ -368,61 +270,21 @@ SerialIoHandlerWin::SerialIoHandlerWin( : SerialIoHandler(port, std::move(ui_thread_task_runner)), base::MessagePumpForIO::IOHandler(FROM_HERE) {} -SerialIoHandlerWin::~SerialIoHandlerWin() { - ui_thread_task_runner()->DeleteSoon(FROM_HERE, helper_); -} +SerialIoHandlerWin::~SerialIoHandlerWin() = default; void SerialIoHandlerWin::OnIOCompleted( base::MessagePumpForIO::IOContext* context, DWORD bytes_transferred, DWORD error) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (context == comm_context_.get()) { - DWORD errors; - if (!ClearCommError(file().GetPlatformFile(), &errors, nullptr)) { - VPLOG(1) << "Failed to clear communication error"; - ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR); - return; - } - - if (errors != 0) { - if (errors & CE_BREAK) { - ReadCompleted(0, mojom::SerialReceiveError::BREAK); - } else if (errors & CE_FRAME) { - ReadCompleted(0, mojom::SerialReceiveError::FRAME_ERROR); - } else if (errors & CE_OVERRUN) { - ReadCompleted(0, mojom::SerialReceiveError::OVERRUN); - } else if (errors & CE_RXOVER) { - ReadCompleted(0, mojom::SerialReceiveError::BUFFER_OVERFLOW); - } else if (errors & CE_RXPARITY) { - ReadCompleted(0, mojom::SerialReceiveError::PARITY_ERROR); - } else { - NOTIMPLEMENTED() << "Unexpected communication error: " << std::hex - << errors; - } - return; - } - - if (read_canceled()) { - ReadCompleted(bytes_transferred, read_cancel_reason()); - } else if (error != ERROR_SUCCESS && error != ERROR_OPERATION_ABORTED) { - VLOG(1) << "Waiting for communcations event failed: " - << logging::SystemErrorCodeToString(error); - ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR); - } else if (pending_read_buffer()) { - BOOL ok = ::ReadFile(file().GetPlatformFile(), pending_read_buffer(), - pending_read_buffer_len(), NULL, - &read_context_->overlapped); - if (!ok && GetLastError() != ERROR_IO_PENDING) { - VPLOG(1) << "Read failed"; - ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR); - } - } - } else if (context == read_context_.get()) { + if (context == read_context_.get()) { if (read_canceled()) { ReadCompleted(bytes_transferred, read_cancel_reason()); } else if (error == ERROR_SUCCESS || error == ERROR_OPERATION_ABORTED) { ReadCompleted(bytes_transferred, mojom::SerialReceiveError::NONE); + } else if (error == ERROR_ACCESS_DENIED || error == ERROR_BAD_COMMAND || + error == ERROR_DEVICE_REMOVED) { + ReadCompleted(0, mojom::SerialReceiveError::DEVICE_LOST); } else { VLOG(1) << "Read failed: " << logging::SystemErrorCodeToString(error); ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR); @@ -433,15 +295,17 @@ void SerialIoHandlerWin::OnIOCompleted( WriteCompleted(0, write_cancel_reason()); } else if (error == ERROR_SUCCESS || error == ERROR_OPERATION_ABORTED) { WriteCompleted(bytes_transferred, mojom::SerialSendError::NONE); + } else if (error == ERROR_GEN_FAILURE) { + WriteCompleted(0, mojom::SerialSendError::DISCONNECTED); } else { VLOG(1) << "Write failed: " << logging::SystemErrorCodeToString(error); WriteCompleted(0, mojom::SerialSendError::SYSTEM_ERROR); if (error == ERROR_GEN_FAILURE && IsReadPending()) { // For devices using drivers such as FTDI, CP2xxx, when device is - // disconnected, the context is comm_context_ and the error is + // disconnected, the context is |read_context_| and the error is // ERROR_OPERATION_ABORTED. // However, for devices using CDC-ACM driver, when device is - // disconnected, the context is write_context_ and the error is + // disconnected, the context is |write_context_| and the error is // ERROR_GEN_FAILURE. In this situation, in addition to a write error // signal, also need to generate a read error signal // mojom::SerialOnReceiveError which will notify the app about the @@ -454,12 +318,51 @@ void SerialIoHandlerWin::OnIOCompleted( } } -bool SerialIoHandlerWin::Flush() const { - if (!PurgeComm(file().GetPlatformFile(), PURGE_RXCLEAR | PURGE_TXCLEAR)) { - VPLOG(1) << "Failed to flush serial port"; - return false; +void SerialIoHandlerWin::ClearPendingError() { + DWORD errors; + if (!ClearCommError(file().GetPlatformFile(), &errors, nullptr)) { + VPLOG(1) << "Failed to clear communication error"; + return; } - return true; + + if (errors & CE_BREAK) { + ReadCompleted(0, mojom::SerialReceiveError::BREAK); + } else if (errors & CE_FRAME) { + ReadCompleted(0, mojom::SerialReceiveError::FRAME_ERROR); + } else if (errors & CE_OVERRUN) { + ReadCompleted(0, mojom::SerialReceiveError::OVERRUN); + } else if (errors & CE_RXOVER) { + ReadCompleted(0, mojom::SerialReceiveError::BUFFER_OVERFLOW); + } else if (errors & CE_RXPARITY) { + ReadCompleted(0, mojom::SerialReceiveError::PARITY_ERROR); + } else if (errors != 0) { + NOTIMPLEMENTED() << "Unexpected communication error: " << std::hex + << errors; + ReadCompleted(0, mojom::SerialReceiveError::SYSTEM_ERROR); + } +} + +void SerialIoHandlerWin::Flush(mojom::SerialPortFlushMode mode) const { + DWORD flags; + switch (mode) { + case mojom::SerialPortFlushMode::kReceiveAndTransmit: + flags = PURGE_RXCLEAR | PURGE_TXCLEAR; + break; + case mojom::SerialPortFlushMode::kReceive: + flags = PURGE_RXCLEAR; + break; + case mojom::SerialPortFlushMode::kTransmit: + flags = PURGE_TXCLEAR; + break; + } + + if (!PurgeComm(file().GetPlatformFile(), flags)) + VPLOG(1) << "Failed to flush serial port"; +} + +void SerialIoHandlerWin::Drain() { + if (!FlushFileBuffers(file().GetPlatformFile())) + VPLOG(1) << "Failed to drain serial port"; } mojom::SerialPortControlSignalsPtr SerialIoHandlerWin::GetControlSignals() diff --git a/chromium/services/device/serial/serial_io_handler_win.h b/chromium/services/device/serial/serial_io_handler_win.h index 9c25e8bfc18..151e127ac99 100644 --- a/chromium/services/device/serial/serial_io_handler_win.h +++ b/chromium/services/device/serial/serial_io_handler_win.h @@ -26,7 +26,8 @@ class SerialIoHandlerWin : public SerialIoHandler, void CancelReadImpl() override; void CancelWriteImpl() override; bool ConfigurePortImpl() override; - bool Flush() const override; + void Flush(mojom::SerialPortFlushMode mode) const override; + void Drain() override; mojom::SerialPortControlSignalsPtr GetControlSignals() const override; bool SetControlSignals( const mojom::SerialHostControlSignals& control_signals) override; @@ -47,10 +48,8 @@ class SerialIoHandlerWin : public SerialIoHandler, DWORD bytes_transfered, DWORD error) override; - void OnDeviceRemoved(const base::string16& device_path); - - // Context used for asynchronous WaitCommEvent calls. - std::unique_ptr<base::MessagePumpForIO::IOContext> comm_context_; + void ClearPendingError(); + void OnDeviceRemoved(const std::wstring& device_path); // Context used for overlapped reads. std::unique_ptr<base::MessagePumpForIO::IOContext> read_context_; @@ -58,14 +57,6 @@ class SerialIoHandlerWin : public SerialIoHandler, // Context used for overlapped writes. std::unique_ptr<base::MessagePumpForIO::IOContext> write_context_; - // Asynchronous event mask state - DWORD event_mask_ = 0; - - // Indicates if a pending read is waiting on initial data arrival via - // WaitCommEvent, as opposed to waiting on actual ReadFile completion - // after a corresponding WaitCommEvent has completed. - bool is_comm_pending_ = false; - // The helper lives on the UI thread and holds a weak reference back to the // handler that owns it. UiThreadHelper* helper_ = nullptr; diff --git a/chromium/services/device/serial/serial_port_impl.cc b/chromium/services/device/serial/serial_port_impl.cc index efef354e9d4..01058c91808 100644 --- a/chromium/services/device/serial/serial_port_impl.cc +++ b/chromium/services/device/serial/serial_port_impl.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/single_thread_task_runner.h" -#include "mojo/public/cpp/bindings/strong_binding.h" #include "services/device/serial/buffer.h" #include "services/device/serial/serial_io_handler.h" @@ -23,17 +22,27 @@ void SerialPortImpl::Create( mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher, scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { // This SerialPortImpl is owned by |receiver| and |watcher|. - new SerialPortImpl(path, std::move(receiver), std::move(watcher), - std::move(ui_task_runner)); + new SerialPortImpl( + device::SerialIoHandler::Create(path, std::move(ui_task_runner)), + std::move(receiver), std::move(watcher)); +} + +// static +void SerialPortImpl::CreateForTesting( + scoped_refptr<SerialIoHandler> io_handler, + mojo::PendingReceiver<mojom::SerialPort> receiver, + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher) { + // This SerialPortImpl is owned by |receiver| and |watcher|. + new SerialPortImpl(std::move(io_handler), std::move(receiver), + std::move(watcher)); } SerialPortImpl::SerialPortImpl( - const base::FilePath& path, + scoped_refptr<SerialIoHandler> io_handler, mojo::PendingReceiver<mojom::SerialPort> receiver, - mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher) : receiver_(this, std::move(receiver)), - io_handler_(device::SerialIoHandler::Create(path, ui_task_runner)), + io_handler_(std::move(io_handler)), watcher_(std::move(watcher)), in_stream_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL), out_stream_watcher_(FROM_HERE, @@ -94,8 +103,62 @@ void SerialPortImpl::StartReading(mojo::ScopedDataPipeProducerHandle producer) { out_stream_watcher_.ArmOrNotify(); } -void SerialPortImpl::Flush(FlushCallback callback) { - std::move(callback).Run(io_handler_->Flush()); +void SerialPortImpl::Flush(mojom::SerialPortFlushMode mode, + FlushCallback callback) { + switch (mode) { + case mojom::SerialPortFlushMode::kReceiveAndTransmit: + // Do nothing. This case exists to support the chrome.serial.flush() + // method. + break; + case mojom::SerialPortFlushMode::kReceive: + io_handler_->CancelRead(mojom::SerialReceiveError::NONE); + break; + case mojom::SerialPortFlushMode::kTransmit: + io_handler_->CancelWrite(mojom::SerialSendError::NONE); + break; + } + + io_handler_->Flush(mode); + + switch (mode) { + case mojom::SerialPortFlushMode::kReceiveAndTransmit: + // Do nothing. This case exists to support the chrome.serial.flush() + // method. + break; + case mojom::SerialPortFlushMode::kReceive: + if (io_handler_->IsReadPending()) { + // Delay closing |out_stream_| because |io_handler_| still holds a + // pointer into the shared memory owned by the pipe. + read_flush_callback_ = std::move(callback); + return; + } + + out_stream_watcher_.Cancel(); + out_stream_.reset(); + break; + case mojom::SerialPortFlushMode::kTransmit: + if (io_handler_->IsWritePending()) { + // Delay closing |in_stream_| because |io_handler_| still holds a + // pointer into the shared memory owned by the pipe. + write_flush_callback_ = std::move(callback); + return; + } + + in_stream_watcher_.Cancel(); + in_stream_.reset(); + break; + } + + std::move(callback).Run(); +} + +void SerialPortImpl::Drain(DrainCallback callback) { + if (!in_stream_) { + std::move(callback).Run(); + return; + } + + drain_callback_ = std::move(callback); } void SerialPortImpl::GetControlSignals(GetControlSignalsCallback callback) { @@ -150,6 +213,11 @@ void SerialPortImpl::WriteToPort(MojoResult result, // The |in_stream_| has been closed. in_stream_watcher_.Cancel(); in_stream_.reset(); + + if (drain_callback_) { + io_handler_->Drain(); + std::move(drain_callback_).Run(); + } return; } // The code should not reach other cases. @@ -204,7 +272,7 @@ void SerialPortImpl::ReadFromPortAndWriteOut( return; } // The code should not reach other cases. - NOTREACHED(); + NOTREACHED() << "Unexpected Mojo result: " << result; } void SerialPortImpl::WriteToOutStream(uint32_t bytes_read, @@ -215,11 +283,20 @@ void SerialPortImpl::WriteToOutStream(uint32_t bytes_read, if (error != mojom::SerialReceiveError::NONE) { out_stream_watcher_.Cancel(); out_stream_.reset(); - if (client_) { + if (client_) client_->OnReadError(error); - } + if (read_flush_callback_) + std::move(read_flush_callback_).Run(); return; } + + if (read_flush_callback_) { + std::move(read_flush_callback_).Run(); + out_stream_watcher_.Cancel(); + out_stream_.reset(); + return; + } + out_stream_watcher_.ArmOrNotify(); } diff --git a/chromium/services/device/serial/serial_port_impl.h b/chromium/services/device/serial/serial_port_impl.h index 2ba5d31d45c..1757a553316 100644 --- a/chromium/services/device/serial/serial_port_impl.h +++ b/chromium/services/device/serial/serial_port_impl.h @@ -39,12 +39,16 @@ class SerialPortImpl : public mojom::SerialPort { mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher, scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); + static void CreateForTesting( + scoped_refptr<SerialIoHandler> io_handler, + mojo::PendingReceiver<mojom::SerialPort> receiver, + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher); + private: SerialPortImpl( - const base::FilePath& path, + scoped_refptr<SerialIoHandler> io_handler, mojo::PendingReceiver<mojom::SerialPort> receiver, - mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher); ~SerialPortImpl() override; // mojom::SerialPort methods: @@ -53,7 +57,8 @@ class SerialPortImpl : public mojom::SerialPort { OpenCallback callback) override; void StartWriting(mojo::ScopedDataPipeConsumerHandle consumer) override; void StartReading(mojo::ScopedDataPipeProducerHandle producer) override; - void Flush(FlushCallback callback) override; + void Flush(mojom::SerialPortFlushMode mode, FlushCallback callback) override; + void Drain(DrainCallback callback) override; void GetControlSignals(GetControlSignalsCallback callback) override; void SetControlSignals(mojom::SerialHostControlSignalsPtr signals, SetControlSignalsCallback callback) override; @@ -85,6 +90,12 @@ class SerialPortImpl : public mojom::SerialPort { mojo::ScopedDataPipeProducerHandle out_stream_; mojo::SimpleWatcher out_stream_watcher_; + // Holds the callback for a flush or drain until pending operations have been + // completed. + FlushCallback read_flush_callback_; + FlushCallback write_flush_callback_; + DrainCallback drain_callback_; + base::WeakPtrFactory<SerialPortImpl> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(SerialPortImpl); }; diff --git a/chromium/services/device/serial/serial_port_impl_unittest.cc b/chromium/services/device/serial/serial_port_impl_unittest.cc index 93c7f0aa317..fdd037496a9 100644 --- a/chromium/services/device/serial/serial_port_impl_unittest.cc +++ b/chromium/services/device/serial/serial_port_impl_unittest.cc @@ -4,23 +4,86 @@ #include "services/device/serial/serial_port_impl.h" -#include "base/macros.h" +#include "base/stl_util.h" +#include "base/test/bind_test_util.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/simple_watcher.h" #include "services/device/device_service_test_base.h" #include "services/device/public/mojom/serial.mojom.h" +#include "services/device/serial/serial_io_handler.h" namespace device { namespace { +class FakeSerialIoHandler : public SerialIoHandler { + public: + FakeSerialIoHandler() + : SerialIoHandler(base::FilePath(), /*ui_thread_task_runner=*/nullptr) {} + + void Open(const mojom::SerialConnectionOptions& options, + OpenCompleteCallback callback) override { + std::move(callback).Run(true); + } + + void Flush(mojom::SerialPortFlushMode mode) const override {} + void Drain() override {} + + mojom::SerialPortControlSignalsPtr GetControlSignals() const override { + return mojom::SerialPortControlSignals::New(); + } + + bool SetControlSignals( + const mojom::SerialHostControlSignals& control_signals) override { + return true; + } + + mojom::SerialConnectionInfoPtr GetPortInfo() const override { + return mojom::SerialConnectionInfo::New(); + } + + void ReadImpl() override {} + + void WriteImpl() override {} + + void CancelReadImpl() override { + QueueReadCompleted(/*bytes_read=*/0, mojom::SerialReceiveError::NONE); + } + + void CancelWriteImpl() override { + QueueWriteCompleted(/*bytes_written=*/0, mojom::SerialSendError::NONE); + } + + bool ConfigurePortImpl() override { return true; } + + private: + ~FakeSerialIoHandler() override = default; +}; + +} // namespace + class SerialPortImplTest : public DeviceServiceTestBase { public: SerialPortImplTest() = default; + SerialPortImplTest(const SerialPortImplTest& other) = delete; + void operator=(const SerialPortImplTest& other) = delete; ~SerialPortImplTest() override = default; - protected: + void CreatePort( + mojo::Remote<mojom::SerialPort>* port, + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher>* watcher) { + mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher_remote; + *watcher = mojo::MakeSelfOwnedReceiver( + std::make_unique<mojom::SerialPortConnectionWatcher>(), + watcher_remote.InitWithNewPipeAndPassReceiver()); + SerialPortImpl::CreateForTesting( + base::MakeRefCounted<FakeSerialIoHandler>(), + port->BindNewPipeAndPassReceiver(), std::move(watcher_remote)); + } + void CreateDataPipe(mojo::ScopedDataPipeProducerHandle* producer, mojo::ScopedDataPipeConsumerHandle* consumer) { MojoCreateDataPipeOptions options; @@ -50,8 +113,6 @@ class SerialPortImplTest : public DeviceServiceTestBase { serial_port->StartWriting(std::move(consumer)); return producer; } - - DISALLOW_COPY_AND_ASSIGN(SerialPortImplTest); }; TEST_F(SerialPortImplTest, StartIoBeforeOpen) { @@ -82,52 +143,121 @@ TEST_F(SerialPortImplTest, StartIoBeforeOpen) { TEST_F(SerialPortImplTest, WatcherClosedWhenPortClosed) { mojo::Remote<mojom::SerialPort> serial_port; - mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher; - auto watcher_receiver = mojo::MakeSelfOwnedReceiver( - std::make_unique<mojom::SerialPortConnectionWatcher>(), - watcher.InitWithNewPipeAndPassReceiver()); - SerialPortImpl::Create( - base::FilePath(), serial_port.BindNewPipeAndPassReceiver(), - std::move(watcher), base::ThreadTaskRunnerHandle::Get()); + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); // To start with both the serial port connection and the connection watcher // connection should remain open. serial_port.FlushForTesting(); EXPECT_TRUE(serial_port.is_connected()); - watcher_receiver->FlushForTesting(); - EXPECT_TRUE(watcher_receiver); + watcher->FlushForTesting(); + EXPECT_TRUE(watcher); // When the serial port connection is closed the watcher connection should be // closed. serial_port.reset(); - watcher_receiver->FlushForTesting(); - EXPECT_FALSE(watcher_receiver); + watcher->FlushForTesting(); + EXPECT_FALSE(watcher); } TEST_F(SerialPortImplTest, PortClosedWhenWatcherClosed) { mojo::Remote<mojom::SerialPort> serial_port; - mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher; - auto watcher_receiver = mojo::MakeSelfOwnedReceiver( - std::make_unique<mojom::SerialPortConnectionWatcher>(), - watcher.InitWithNewPipeAndPassReceiver()); - SerialPortImpl::Create( - base::FilePath(), serial_port.BindNewPipeAndPassReceiver(), - std::move(watcher), base::ThreadTaskRunnerHandle::Get()); + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); // To start with both the serial port connection and the connection watcher // connection should remain open. serial_port.FlushForTesting(); EXPECT_TRUE(serial_port.is_connected()); - watcher_receiver->FlushForTesting(); - EXPECT_TRUE(watcher_receiver); + watcher->FlushForTesting(); + EXPECT_TRUE(watcher); // When the watcher connection is closed, for safety, the serial port // connection should also be closed. - watcher_receiver->Close(); + watcher->Close(); serial_port.FlushForTesting(); EXPECT_FALSE(serial_port.is_connected()); } -} // namespace +TEST_F(SerialPortImplTest, FlushRead) { + mojo::Remote<mojom::SerialPort> serial_port; + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); + + mojo::ScopedDataPipeConsumerHandle consumer = StartReading(serial_port.get()); + + // Calling Flush(kReceive) should cause the data pipe to close. + base::RunLoop watcher_loop; + mojo::SimpleWatcher pipe_watcher( + FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC); + EXPECT_EQ(pipe_watcher.Watch(consumer.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + base::BindLambdaForTesting( + [&](MojoResult result, + const mojo::HandleSignalsState& state) { + EXPECT_EQ(result, MOJO_RESULT_OK); + EXPECT_TRUE(state.peer_closed()); + watcher_loop.Quit(); + })), + MOJO_RESULT_OK); + + base::RunLoop loop; + serial_port->Flush(mojom::SerialPortFlushMode::kReceive, loop.QuitClosure()); + loop.Run(); + watcher_loop.Run(); +} + +TEST_F(SerialPortImplTest, FlushWrite) { + mojo::Remote<mojom::SerialPort> serial_port; + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); + + mojo::ScopedDataPipeProducerHandle producer = StartWriting(serial_port.get()); + + // Calling Flush(kTransmit) should cause the data pipe to close. + base::RunLoop watcher_loop; + mojo::SimpleWatcher pipe_watcher( + FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC); + EXPECT_EQ(pipe_watcher.Watch(producer.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, + base::BindLambdaForTesting( + [&](MojoResult result, + const mojo::HandleSignalsState& state) { + EXPECT_EQ(result, MOJO_RESULT_OK); + EXPECT_TRUE(state.peer_closed()); + watcher_loop.Quit(); + })), + MOJO_RESULT_OK); + + base::RunLoop loop; + serial_port->Flush(mojom::SerialPortFlushMode::kTransmit, loop.QuitClosure()); + loop.Run(); + watcher_loop.Run(); +} + +TEST_F(SerialPortImplTest, Drain) { + mojo::Remote<mojom::SerialPort> serial_port; + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); + + mojo::ScopedDataPipeProducerHandle producer = StartWriting(serial_port.get()); + + // Drain() will wait for the data pipe to close before replying. + producer.reset(); + + base::RunLoop loop; + serial_port->Drain(loop.QuitClosure()); + loop.Run(); +} + +TEST_F(SerialPortImplTest, Close) { + mojo::Remote<mojom::SerialPort> serial_port; + mojo::SelfOwnedReceiverRef<mojom::SerialPortConnectionWatcher> watcher; + CreatePort(&serial_port, &watcher); + + base::RunLoop loop; + serial_port->Close(loop.QuitClosure()); + loop.Run(); +} } // namespace device diff --git a/chromium/services/device/serial/serial_port_manager_impl.cc b/chromium/services/device/serial/serial_port_manager_impl.cc index b893f4e2e2a..bdfecb35f78 100644 --- a/chromium/services/device/serial/serial_port_manager_impl.cc +++ b/chromium/services/device/serial/serial_port_manager_impl.cc @@ -6,10 +6,14 @@ #include <string> #include <utility> +#include <vector> #include "base/bind.h" +#include "base/command_line.h" #include "base/sequenced_task_runner.h" -#include "mojo/public/cpp/bindings/strong_binding.h" +#include "device/bluetooth/bluetooth_adapter_factory.h" +#include "services/device/public/cpp/serial/serial_switches.h" +#include "services/device/serial/bluetooth_serial_device_enumerator.h" #include "services/device/serial/serial_device_enumerator.h" #include "services/device/serial/serial_port_impl.h" @@ -35,6 +39,14 @@ void SerialPortManagerImpl::SetSerialEnumeratorForTesting( observed_enumerator_.Add(enumerator_.get()); } +void SerialPortManagerImpl::SetBluetoothSerialEnumeratorForTesting( + std::unique_ptr<BluetoothSerialDeviceEnumerator> + fake_bluetooth_enumerator) { + DCHECK(fake_bluetooth_enumerator); + bluetooth_enumerator_ = std::move(fake_bluetooth_enumerator); + observed_enumerator_.Add(bluetooth_enumerator_.get()); +} + void SerialPortManagerImpl::SetClient( mojo::PendingRemote<mojom::SerialPortManagerClient> client) { clients_.Add(std::move(client)); @@ -45,7 +57,21 @@ void SerialPortManagerImpl::GetDevices(GetDevicesCallback callback) { enumerator_ = SerialDeviceEnumerator::Create(ui_task_runner_); observed_enumerator_.Add(enumerator_.get()); } - std::move(callback).Run(enumerator_->GetDevices()); + auto devices = enumerator_->GetDevices(); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableBluetoothSerialPortProfileInSerialApi)) { + if (!bluetooth_enumerator_) { + bluetooth_enumerator_ = + std::make_unique<BluetoothSerialDeviceEnumerator>(); + observed_enumerator_.Add(bluetooth_enumerator_.get()); + } + auto bluetooth_devices = bluetooth_enumerator_->GetDevices(); + devices.insert(devices.end(), + std::make_move_iterator(bluetooth_devices.begin()), + std::make_move_iterator(bluetooth_devices.end())); + } + + std::move(callback).Run(std::move(devices)); } void SerialPortManagerImpl::GetPort( diff --git a/chromium/services/device/serial/serial_port_manager_impl.h b/chromium/services/device/serial/serial_port_manager_impl.h index 546e9a25fcb..7e918711fbf 100644 --- a/chromium/services/device/serial/serial_port_manager_impl.h +++ b/chromium/services/device/serial/serial_port_manager_impl.h @@ -15,6 +15,7 @@ #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/remote_set.h" #include "services/device/public/mojom/serial.mojom.h" +#include "services/device/serial/bluetooth_serial_device_enumerator.h" #include "services/device/serial/serial_device_enumerator.h" namespace base { @@ -38,6 +39,9 @@ class SerialPortManagerImpl : public mojom::SerialPortManager, void Bind(mojo::PendingReceiver<mojom::SerialPortManager> receiver); void SetSerialEnumeratorForTesting( std::unique_ptr<SerialDeviceEnumerator> fake_enumerator); + void SetBluetoothSerialEnumeratorForTesting( + std::unique_ptr<BluetoothSerialDeviceEnumerator> + fake_bluetooth_enumerator); private: // mojom::SerialPortManager methods: @@ -55,6 +59,7 @@ class SerialPortManagerImpl : public mojom::SerialPortManager, void OnPortRemoved(const mojom::SerialPortInfo& port) override; std::unique_ptr<SerialDeviceEnumerator> enumerator_; + std::unique_ptr<BluetoothSerialDeviceEnumerator> bluetooth_enumerator_; ScopedObserver<SerialDeviceEnumerator, SerialDeviceEnumerator::Observer> observed_enumerator_{this}; diff --git a/chromium/services/device/serial/serial_port_manager_impl_unittest.cc b/chromium/services/device/serial/serial_port_manager_impl_unittest.cc index 3b5b4da545f..05a599102b6 100644 --- a/chromium/services/device/serial/serial_port_manager_impl_unittest.cc +++ b/chromium/services/device/serial/serial_port_manager_impl_unittest.cc @@ -10,16 +10,23 @@ #include <vector> #include "base/bind.h" +#include "base/command_line.h" #include "base/macros.h" #include "base/task/post_task.h" #include "base/test/bind_test_util.h" #include "base/threading/thread.h" +#include "device/bluetooth/bluetooth_adapter_factory.h" +#include "device/bluetooth/public/cpp/bluetooth_uuid.h" +#include "device/bluetooth/test/mock_bluetooth_adapter.h" +#include "device/bluetooth/test/mock_bluetooth_device.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "services/device/device_service_test_base.h" +#include "services/device/public/cpp/serial/serial_switches.h" #include "services/device/public/mojom/serial.mojom.h" +#include "services/device/serial/bluetooth_serial_device_enumerator.h" #include "services/device/serial/fake_serial_device_enumerator.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -33,6 +40,7 @@ namespace { const base::FilePath kFakeDevicePath1(FILE_PATH_LITERAL("/dev/fakeserialmojo")); const base::FilePath kFakeDevicePath2(FILE_PATH_LITERAL("\\\\COM800\\")); +constexpr char kDeviceAddress[] = "00:00:00:00:00:00"; class MockSerialPortManagerClient : public mojom::SerialPortManagerClient { public: @@ -72,8 +80,37 @@ class SerialPortManagerImplTest : public DeviceServiceTestBase { ~SerialPortManagerImplTest() override = default; + // Since not all functions need to use a MockBluetoothAdapter, this function + // is called at the beginning of test cases that do require a + // MockBluetoothAdapter. + void SetupBluetoothEnumerator() { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableBluetoothSerialPortProfileInSerialApi); + + ON_CALL(*adapter_, GetDevices()) + .WillByDefault( + Invoke(adapter_.get(), &MockBluetoothAdapter::GetConstMockDevices)); + device::BluetoothAdapterFactory::SetAdapterForTesting(adapter_); + + auto mock_device = std::make_unique<MockBluetoothDevice>( + adapter_.get(), 0, "Test Device", kDeviceAddress, false, false); + static const BluetoothUUID kSerialPortProfileUUID("1101"); + mock_device->AddUUID(kSerialPortProfileUUID); + adapter_->AddMockDevice(std::move(mock_device)); + + auto bluetooth_enumerator = + std::make_unique<BluetoothSerialDeviceEnumerator>(); + bluetooth_enumerator_ = bluetooth_enumerator.get(); + + manager_->SetBluetoothSerialEnumeratorForTesting( + std::move(bluetooth_enumerator)); + } + protected: FakeSerialEnumerator* enumerator_; + BluetoothSerialDeviceEnumerator* bluetooth_enumerator_; + scoped_refptr<MockBluetoothAdapter> adapter_ = + base::MakeRefCounted<MockBluetoothAdapter>(); void Bind(mojo::PendingReceiver<mojom::SerialPortManager> receiver) { manager_->Bind(std::move(receiver)); @@ -88,6 +125,8 @@ class SerialPortManagerImplTest : public DeviceServiceTestBase { // This is to simply test that we can enumerate devices on the platform without // hanging or crashing. TEST_F(SerialPortManagerImplTest, SimpleConnectTest) { + // DeviceService has its own instance of SerialPortManagerImpl that is used to + // bind the receiver over the one created for this test. mojo::Remote<mojom::SerialPortManager> port_manager; device_service()->BindSerialPortManager( port_manager.BindNewPipeAndPassReceiver()); @@ -112,10 +151,14 @@ TEST_F(SerialPortManagerImplTest, SimpleConnectTest) { } TEST_F(SerialPortManagerImplTest, GetDevices) { + SetupBluetoothEnumerator(); mojo::Remote<mojom::SerialPortManager> port_manager; Bind(port_manager.BindNewPipeAndPassReceiver()); - const std::set<base::FilePath> expected_paths = {kFakeDevicePath1, - kFakeDevicePath2}; + const std::string address_identifier = + std::string(kDeviceAddress) + "-Identifier"; + const std::set<base::FilePath> expected_paths = { + kFakeDevicePath1, kFakeDevicePath2, + base::FilePath::FromUTF8Unsafe(address_identifier)}; base::RunLoop loop; port_manager->GetDevices(base::BindLambdaForTesting( @@ -131,6 +174,7 @@ TEST_F(SerialPortManagerImplTest, GetDevices) { } TEST_F(SerialPortManagerImplTest, PortRemovedAndAdded) { + SetupBluetoothEnumerator(); mojo::Remote<mojom::SerialPortManager> port_manager; Bind(port_manager.BindNewPipeAndPassReceiver()); @@ -180,6 +224,7 @@ TEST_F(SerialPortManagerImplTest, PortRemovedAndAdded) { } TEST_F(SerialPortManagerImplTest, GetPort) { + SetupBluetoothEnumerator(); mojo::Remote<mojom::SerialPortManager> port_manager; Bind(port_manager.BindNewPipeAndPassReceiver()); @@ -202,4 +247,69 @@ TEST_F(SerialPortManagerImplTest, GetPort) { loop.Run(); } +TEST_F(SerialPortManagerImplTest, BluetoothPortRemovedAndAdded) { + SetupBluetoothEnumerator(); + mojo::Remote<mojom::SerialPortManager> port_manager; + Bind(port_manager.BindNewPipeAndPassReceiver()); + + MockSerialPortManagerClient client; + port_manager->SetClient(client.BindNewPipeAndPassRemote()); + + const std::string address_identifier = + std::string(kDeviceAddress) + "-Identifier"; + base::UnguessableToken port1_token; + { + base::RunLoop run_loop; + port_manager->GetDevices(base::BindLambdaForTesting( + [&](std::vector<mojom::SerialPortInfoPtr> results) { + for (const auto& port : results) { + if (port->path == + base::FilePath::FromUTF8Unsafe(address_identifier)) { + port1_token = port->token; + break; + } + } + run_loop.Quit(); + })); + run_loop.Run(); + } + ASSERT_FALSE(port1_token.is_empty()); + + bluetooth_enumerator_->DeviceRemoved( + adapter_.get(), adapter_->RemoveMockDevice(kDeviceAddress).get()); + { + base::RunLoop run_loop; + EXPECT_CALL(client, OnPortRemoved(_)) + .WillOnce(Invoke([&](mojom::SerialPortInfoPtr port) { + EXPECT_EQ(port1_token, port->token); + EXPECT_EQ(port->path, + base::FilePath::FromUTF8Unsafe(address_identifier)); + EXPECT_EQ(mojom::DeviceType::SPP_DEVICE, port->type); + run_loop.Quit(); + })); + run_loop.Run(); + } + + auto mock_device = std::make_unique<MockBluetoothDevice>( + adapter_.get(), 0, "Test Device", kDeviceAddress, false, false); + static const BluetoothUUID kSerialPortProfileUUID("1101"); + mock_device->AddUUID(kSerialPortProfileUUID); + MockBluetoothDevice* mock_device_ptr = mock_device.get(); + adapter_->AddMockDevice(std::move(mock_device)); + + bluetooth_enumerator_->DeviceAdded(adapter_.get(), mock_device_ptr); + { + base::RunLoop run_loop; + EXPECT_CALL(client, OnPortAdded(_)) + .WillOnce(Invoke([&](mojom::SerialPortInfoPtr port) { + EXPECT_NE(port1_token, port->token); + EXPECT_EQ(port->path, + base::FilePath::FromUTF8Unsafe(address_identifier)); + EXPECT_EQ(mojom::DeviceType::SPP_DEVICE, port->type); + run_loop.Quit(); + })); + run_loop.Run(); + } +} + } // namespace device diff --git a/chromium/services/device/time_zone_monitor/BUILD.gn b/chromium/services/device/time_zone_monitor/BUILD.gn index 440344d3ef5..a46402d2f4e 100644 --- a/chromium/services/device/time_zone_monitor/BUILD.gn +++ b/chromium/services/device/time_zone_monitor/BUILD.gn @@ -44,7 +44,7 @@ source_set("time_zone_monitor") { } if (is_mac) { - libs = [ "Foundation.framework" ] + frameworks = [ "Foundation.framework" ] } if (is_fuchsia) { diff --git a/chromium/services/device/usb/BUILD.gn b/chromium/services/device/usb/BUILD.gn index d754b63ec24..5b7ea62333d 100644 --- a/chromium/services/device/usb/BUILD.gn +++ b/chromium/services/device/usb/BUILD.gn @@ -32,10 +32,14 @@ static_library("usb") { "usb_device_handle.h", "usb_device_handle_android.cc", "usb_device_handle_android.h", + "usb_device_handle_mac.cc", + "usb_device_handle_mac.h", "usb_device_handle_win.cc", "usb_device_handle_win.h", "usb_device_linux.cc", "usb_device_linux.h", + "usb_device_mac.cc", + "usb_device_mac.h", "usb_device_win.cc", "usb_device_win.h", "usb_endpoint_android.cc", @@ -46,6 +50,8 @@ static_library("usb") { "usb_service.h", "usb_service_android.cc", "usb_service_android.h", + "usb_service_mac.cc", + "usb_service_mac.h", "usb_service_win.cc", "usb_service_win.h", "webusb_descriptors.cc", diff --git a/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbConfiguration.java b/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbConfiguration.java index 90371e53c0e..aeaae0e732b 100644 --- a/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbConfiguration.java +++ b/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbConfiguration.java @@ -4,10 +4,8 @@ package org.chromium.device.usb; -import android.annotation.TargetApi; import android.hardware.usb.UsbConfiguration; import android.hardware.usb.UsbInterface; -import android.os.Build; import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; @@ -20,7 +18,6 @@ import org.chromium.base.annotations.JNINamespace; * Lifetime is controlled by device::UsbConfigurationAndroid. */ @JNINamespace("device") -@TargetApi(Build.VERSION_CODES.LOLLIPOP) final class ChromeUsbConfiguration { private static final String TAG = "Usb"; diff --git a/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbDevice.java b/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbDevice.java index d407ef71427..223b8de337e 100644 --- a/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbDevice.java +++ b/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbDevice.java @@ -7,7 +7,6 @@ package org.chromium.device.usb; import android.annotation.TargetApi; import android.hardware.usb.UsbConfiguration; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbInterface; import android.os.Build; import org.chromium.base.Log; @@ -83,25 +82,21 @@ final class ChromeUsbDevice { return Integer.parseInt(parts[0]) << 8 | Integer.parseInt(parts[1]); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @CalledByNative private String getManufacturerName() { return mDevice.getManufacturerName(); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @CalledByNative private String getProductName() { return mDevice.getProductName(); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @CalledByNative private String getSerialNumber() { return mDevice.getSerialNumber(); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @CalledByNative private UsbConfiguration[] getConfigurations() { int count = mDevice.getConfigurationCount(); @@ -111,14 +106,4 @@ final class ChromeUsbDevice { } return configurations; } - - @CalledByNative - private UsbInterface[] getInterfaces() { - int count = mDevice.getInterfaceCount(); - UsbInterface[] interfaces = new UsbInterface[count]; - for (int i = 0; i < count; ++i) { - interfaces[i] = mDevice.getInterface(i); - } - return interfaces; - } } diff --git a/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbInterface.java b/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbInterface.java index 1637aae0f61..cd4f49270ed 100644 --- a/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbInterface.java +++ b/chromium/services/device/usb/android/java/src/org/chromium/device/usb/ChromeUsbInterface.java @@ -4,10 +4,8 @@ package org.chromium.device.usb; -import android.annotation.TargetApi; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; -import android.os.Build; import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; @@ -40,7 +38,6 @@ final class ChromeUsbInterface { return mInterface.getId(); } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @CalledByNative private int getAlternateSetting() { return mInterface.getAlternateSetting(); diff --git a/chromium/services/device/usb/mojo/device_impl.cc b/chromium/services/device/usb/mojo/device_impl.cc index 1e7a6a798cf..bc708148647 100644 --- a/chromium/services/device/usb/mojo/device_impl.cc +++ b/chromium/services/device/usb/mojo/device_impl.cc @@ -160,6 +160,7 @@ void DeviceImpl::OnOpen(base::WeakPtr<DeviceImpl> self, return; } + self->opening_ = false; self->device_handle_ = std::move(handle); if (self->device_handle_ && self->client_) self->client_->OnDeviceOpened(); @@ -175,16 +176,19 @@ void DeviceImpl::OnPermissionGrantedForOpen(OpenCallback callback, device_->Open(base::BindOnce( &DeviceImpl::OnOpen, weak_factory_.GetWeakPtr(), std::move(callback))); } else { + opening_ = false; std::move(callback).Run(mojom::UsbOpenDeviceError::ACCESS_DENIED); } } void DeviceImpl::Open(OpenCallback callback) { - if (device_handle_) { + if (opening_ || device_handle_) { std::move(callback).Run(mojom::UsbOpenDeviceError::ALREADY_OPEN); return; } + opening_ = true; + if (!device_->permission_granted()) { device_->RequestPermission( base::BindOnce(&DeviceImpl::OnPermissionGrantedForOpen, diff --git a/chromium/services/device/usb/mojo/device_impl.h b/chromium/services/device/usb/mojo/device_impl.h index ca3bffe4392..0965ca72663 100644 --- a/chromium/services/device/usb/mojo/device_impl.h +++ b/chromium/services/device/usb/mojo/device_impl.h @@ -106,7 +106,9 @@ class DeviceImpl : public mojom::UsbDevice, public device::UsbDevice::Observer { ScopedObserver<device::UsbDevice, device::UsbDevice::Observer> observer_; // The device handle. Will be null before the device is opened and after it - // has been closed. + // has been closed. |opening_| is set to true while the asynchronous open is + // in progress. + bool opening_ = false; scoped_refptr<UsbDeviceHandle> device_handle_; mojo::SelfOwnedReceiverRef<mojom::UsbDevice> receiver_; diff --git a/chromium/services/device/usb/mojo/device_impl_unittest.cc b/chromium/services/device/usb/mojo/device_impl_unittest.cc index f0cd0eab4cf..4987fbe5519 100644 --- a/chromium/services/device/usb/mojo/device_impl_unittest.cc +++ b/chromium/services/device/usb/mojo/device_impl_unittest.cc @@ -22,8 +22,8 @@ #include "base/memory/ref_counted_memory.h" #include "base/run_loop.h" #include "base/stl_util.h" +#include "base/test/bind_test_util.h" #include "base/test/task_environment.h" -#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/device/usb/mock_usb_device.h" @@ -265,7 +265,10 @@ class USBDeviceImplTest : public testing::Test { void OpenMockHandle(UsbDevice::OpenCallback& callback) { EXPECT_FALSE(is_device_open_); is_device_open_ = true; - std::move(callback).Run(mock_handle_); + // Simulate the asynchronous device opening process. + base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::BindOnce(std::move(callback), mock_handle_), + base::TimeDelta::FromMilliseconds(1)); } void CloseMockHandle() { @@ -515,17 +518,39 @@ TEST_F(USBDeviceImplTest, OpenFailure) { GetMockDeviceProxy(device_client.CreateInterfacePtrAndBind()); EXPECT_CALL(mock_device(), OpenInternal(_)) - .WillOnce(Invoke([](UsbDevice::OpenCallback& callback) { + .WillOnce([](UsbDevice::OpenCallback& callback) { std::move(callback).Run(nullptr); - })); + }); EXPECT_CALL(device_client, OnDeviceOpened()).Times(0); EXPECT_CALL(device_client, OnDeviceClosed()).Times(0); - base::RunLoop loop; - device->Open(base::BindOnce(&ExpectOpenAndThen, - mojom::UsbOpenDeviceError::ACCESS_DENIED, - loop.QuitClosure())); - loop.Run(); + { + base::RunLoop loop; + device->Open( + base::BindLambdaForTesting([&](mojom::UsbOpenDeviceError result) { + EXPECT_EQ(result, mojom::UsbOpenDeviceError::ACCESS_DENIED); + loop.Quit(); + })); + loop.Run(); + } + + // A second attempt can succeed. + EXPECT_CALL(mock_device(), OpenInternal(_)); + EXPECT_CALL(device_client, OnDeviceOpened()); + EXPECT_CALL(device_client, OnDeviceClosed()); + + { + base::RunLoop loop; + device->Open( + base::BindLambdaForTesting([&](mojom::UsbOpenDeviceError result) { + EXPECT_EQ(result, mojom::UsbOpenDeviceError::OK); + loop.Quit(); + })); + loop.Run(); + } + + device.reset(); + base::RunLoop().RunUntilIdle(); } TEST_F(USBDeviceImplTest, OpenDelayedFailure) { @@ -549,6 +574,24 @@ TEST_F(USBDeviceImplTest, OpenDelayedFailure) { std::move(saved_callback).Run(nullptr); } +TEST_F(USBDeviceImplTest, MultipleOpenNotAllowed) { + MockUsbDeviceClient device_client; + mojo::Remote<mojom::UsbDevice> device = + GetMockDeviceProxy(device_client.CreateInterfacePtrAndBind()); + + base::RunLoop loop; + device->Open( + base::BindLambdaForTesting([&](mojom::UsbOpenDeviceError result) { + EXPECT_EQ(result, mojom::UsbOpenDeviceError::OK); + })); + device->Open( + base::BindLambdaForTesting([&](mojom::UsbOpenDeviceError result) { + EXPECT_EQ(result, mojom::UsbOpenDeviceError::ALREADY_OPEN); + loop.Quit(); + })); + loop.Run(); +} + TEST_F(USBDeviceImplTest, Close) { MockUsbDeviceClient device_client; mojo::Remote<mojom::UsbDevice> device = diff --git a/chromium/services/device/usb/mojo/device_manager_impl_unittest.cc b/chromium/services/device/usb/mojo/device_manager_impl_unittest.cc index b2869d0c9b6..d3a845fa2fd 100644 --- a/chromium/services/device/usb/mojo/device_manager_impl_unittest.cc +++ b/chromium/services/device/usb/mojo/device_manager_impl_unittest.cc @@ -18,7 +18,6 @@ #include "base/test/task_environment.h" #include "base/threading/thread_task_runner_handle.h" #include "mojo/public/cpp/bindings/associated_receiver.h" -#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/remote.h" #include "services/device/public/mojom/usb_enumeration_options.mojom.h" #include "services/device/public/mojom/usb_manager_client.mojom.h" diff --git a/chromium/services/device/usb/usb_descriptors.cc b/chromium/services/device/usb/usb_descriptors.cc index 65b28e9c4b4..a707fb0336f 100644 --- a/chromium/services/device/usb/usb_descriptors.cc +++ b/chromium/services/device/usb/usb_descriptors.cc @@ -110,8 +110,7 @@ void OnReadConfigDescriptor(UsbDeviceDescriptor* desc, scoped_refptr<base::RefCountedBytes> buffer, size_t length) { if (status == UsbTransferStatus::COMPLETED) { - if (!desc->Parse( - std::vector<uint8_t>(buffer->front(), buffer->front() + length))) { + if (!desc->Parse(base::make_span(buffer->front(), length))) { LOG(ERROR) << "Failed to parse configuration descriptor."; } } else { @@ -157,8 +156,7 @@ void OnReadDeviceDescriptor( } std::unique_ptr<UsbDeviceDescriptor> desc(new UsbDeviceDescriptor()); - if (!desc->Parse( - std::vector<uint8_t>(buffer->front(), buffer->front() + length))) { + if (!desc->Parse(base::make_span(buffer->front(), length))) { LOG(ERROR) << "Device descriptor parsing error."; std::move(callback).Run(nullptr); return; @@ -262,7 +260,7 @@ UsbDeviceDescriptor::UsbDeviceDescriptor() UsbDeviceDescriptor::~UsbDeviceDescriptor() = default; -bool UsbDeviceDescriptor::Parse(const std::vector<uint8_t>& buffer) { +bool UsbDeviceDescriptor::Parse(base::span<const uint8_t> buffer) { mojom::UsbConfigurationInfo* last_config = nullptr; mojom::UsbInterfaceInfo* last_interface = nullptr; mojom::UsbEndpointInfo* last_endpoint = nullptr; diff --git a/chromium/services/device/usb/usb_descriptors.h b/chromium/services/device/usb/usb_descriptors.h index ba173395e69..482bf4fa505 100644 --- a/chromium/services/device/usb/usb_descriptors.h +++ b/chromium/services/device/usb/usb_descriptors.h @@ -41,7 +41,7 @@ struct UsbDeviceDescriptor { // be used to populate this struct's fields. This function may be called more // than once (i.e. for multiple buffers containing a configuration descriptor // each). - bool Parse(const std::vector<uint8_t>& buffer); + bool Parse(base::span<const uint8_t> buffer); uint8_t i_manufacturer = 0; uint8_t i_product = 0; diff --git a/chromium/services/device/usb/usb_device.cc b/chromium/services/device/usb/usb_device.cc index 40eabd6c7e0..aaf39c26d7c 100644 --- a/chromium/services/device/usb/usb_device.cc +++ b/chromium/services/device/usb/usb_device.cc @@ -118,7 +118,7 @@ void UsbDevice::OnDisconnect() { // Swap out the handle list as HandleClosed() will try to modify it. std::list<UsbDeviceHandle*> handles; handles.swap(handles_); - for (auto* handle : handles_) + for (auto* handle : handles) handle->Close(); } diff --git a/chromium/services/device/usb/usb_device.h b/chromium/services/device/usb/usb_device.h index 35b6c5d6de4..15d7430e890 100644 --- a/chromium/services/device/usb/usb_device.h +++ b/chromium/services/device/usb/usb_device.h @@ -145,11 +145,13 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> { private: friend class base::RefCountedThreadSafe<UsbDevice>; friend class UsbDeviceHandleImpl; + friend class UsbDeviceHandleMac; friend class UsbDeviceHandleUsbfs; friend class UsbDeviceHandleWin; friend class UsbServiceAndroid; friend class UsbServiceImpl; friend class UsbServiceLinux; + friend class UsbServiceMac; friend class UsbServiceWin; void OnDisconnect(); diff --git a/chromium/services/device/usb/usb_device_android.cc b/chromium/services/device/usb/usb_device_android.cc index c4c9283b2ee..24a0ae1d975 100644 --- a/chromium/services/device/usb/usb_device_android.cc +++ b/chromium/services/device/usb/usb_device_android.cc @@ -41,25 +41,26 @@ scoped_refptr<UsbDeviceAndroid> UsbDeviceAndroid::Create( if (build_info->sdk_int() >= base::android::SDK_VERSION_MARSHMALLOW) device_version = Java_ChromeUsbDevice_getDeviceVersion(env, wrapper); - base::string16 manufacturer_string, product_string, serial_number; - if (build_info->sdk_int() >= base::android::SDK_VERSION_LOLLIPOP) { - ScopedJavaLocalRef<jstring> manufacturer_jstring = - Java_ChromeUsbDevice_getManufacturerName(env, wrapper); - if (!manufacturer_jstring.is_null()) - manufacturer_string = ConvertJavaStringToUTF16(env, manufacturer_jstring); - ScopedJavaLocalRef<jstring> product_jstring = - Java_ChromeUsbDevice_getProductName(env, wrapper); - if (!product_jstring.is_null()) - product_string = ConvertJavaStringToUTF16(env, product_jstring); - - // Reading the serial number requires device access permission when - // targeting the Q SDK. - if (service->HasDevicePermission(wrapper) || !build_info->is_at_least_q()) { - ScopedJavaLocalRef<jstring> serial_jstring = - Java_ChromeUsbDevice_getSerialNumber(env, wrapper); - if (!serial_jstring.is_null()) - serial_number = ConvertJavaStringToUTF16(env, serial_jstring); - } + base::string16 manufacturer_string; + ScopedJavaLocalRef<jstring> manufacturer_jstring = + Java_ChromeUsbDevice_getManufacturerName(env, wrapper); + if (!manufacturer_jstring.is_null()) + manufacturer_string = ConvertJavaStringToUTF16(env, manufacturer_jstring); + + base::string16 product_string; + ScopedJavaLocalRef<jstring> product_jstring = + Java_ChromeUsbDevice_getProductName(env, wrapper); + if (!product_jstring.is_null()) + product_string = ConvertJavaStringToUTF16(env, product_jstring); + + // Reading the serial number requires device access permission when + // targeting the Q SDK. + base::string16 serial_number; + if (service->HasDevicePermission(wrapper) || !build_info->is_at_least_q()) { + ScopedJavaLocalRef<jstring> serial_jstring = + Java_ChromeUsbDevice_getSerialNumber(env, wrapper); + if (!serial_jstring.is_null()) + serial_number = ConvertJavaStringToUTF16(env, serial_jstring); } return base::WrapRefCounted(new UsbDeviceAndroid( @@ -134,33 +135,12 @@ UsbDeviceAndroid::UsbDeviceAndroid(JNIEnv* env, device_id_(Java_ChromeUsbDevice_getDeviceId(env, wrapper)), service_(service), j_object_(wrapper) { - if (base::android::BuildInfo::GetInstance()->sdk_int() >= - base::android::SDK_VERSION_LOLLIPOP) { - JavaObjectArrayReader<jobject> configurations( - Java_ChromeUsbDevice_getConfigurations(env, j_object_)); - device_info_->configurations.reserve(configurations.size()); - for (auto config : configurations) { - device_info_->configurations.push_back( - UsbConfigurationAndroid::Convert(env, config)); - } - } else { - // Pre-lollipop only the first configuration was supported. Build a basic - // configuration out of the available interfaces. - mojom::UsbConfigurationInfoPtr config = BuildUsbConfigurationInfoPtr( - 1, // Configuration value, reasonable guess. - false, // Self powered, arbitrary default. - false, // Remote wakeup, rbitrary default. - 0); // Maximum power, aitrary default. - - JavaObjectArrayReader<jobject> interfaces( - Java_ChromeUsbDevice_getInterfaces(env, wrapper)); - config->interfaces.reserve(interfaces.size()); - for (auto interface : interfaces) { - config->interfaces.push_back( - UsbInterfaceAndroid::Convert(env, interface)); - } - AggregateInterfacesForConfig(config.get()); - device_info_->configurations.push_back(std::move(config)); + JavaObjectArrayReader<jobject> configs( + Java_ChromeUsbDevice_getConfigurations(env, j_object_)); + device_info_->configurations.reserve(configs.size()); + for (auto config : configs) { + device_info_->configurations.push_back( + UsbConfigurationAndroid::Convert(env, config)); } if (configurations().size() > 0) diff --git a/chromium/services/device/usb/usb_device_handle_mac.cc b/chromium/services/device/usb/usb_device_handle_mac.cc new file mode 100644 index 00000000000..c91439bce10 --- /dev/null +++ b/chromium/services/device/usb/usb_device_handle_mac.cc @@ -0,0 +1,925 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/usb/usb_device_handle_mac.h" + +#include <IOKit/IOCFBundle.h> +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOReturn.h> +#include <IOKit/IOTypes.h> +#include <IOKit/usb/IOUSBLib.h> +#include <MacTypes.h> + +#include <memory> +#include <numeric> +#include <utility> + +#include "base/mac/scoped_ioobject.h" +#include "base/mac/scoped_ioplugininterface.h" +#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_memory.h" +#include "base/strings/string_number_conversions.h" +#include "components/device_event_log/device_event_log.h" +#include "services/device/public/cpp/usb/usb_utils.h" +#include "services/device/usb/usb_device_mac.h" + +namespace device { + +struct Transfer { + UsbDeviceHandleMac::TransferCallback generic_callback; + scoped_refptr<UsbDeviceHandleMac> handle; + scoped_refptr<base::RefCountedBytes> buffer; + std::vector<uint32_t> packet_lengths; + std::vector<IOUSBIsocFrame> frame_list; + mojom::UsbTransferType type; + UsbDeviceHandleMac::IsochronousTransferCallback isochronous_callback; +}; + +namespace { + +// This is the bit 7 of the request type. +enum class EndpointDirection : uint8_t { kIn = 0x80, kOut = 0x00 }; + +// These are bits 5 and 6 of the request type. +enum class RequestType : uint8_t { + kStandard = 0x00, + kClass = 0x20, + kVendor = 0x40, + kReserved = 0x60 +}; + +// These are bits 0 and 1 of the request type. +enum class RequestRecipient : uint8_t { + kDevice = 0x00, + kInterface = 0x01, + kEndpoint = 0x02, + kOther = 0x03, +}; + +mojom::UsbTransferStatus ConvertTransferStatus(IOReturn status) { + switch (status) { + // kIOReturnUnderrun can be ignored because the lower-than-expected transfer + // size is reported alongside the COMPLETED status. + case kIOReturnUnderrun: + case kIOReturnSuccess: + return mojom::UsbTransferStatus::COMPLETED; + case kIOUSBTransactionTimeout: + return mojom::UsbTransferStatus::TIMEOUT; + case kIOUSBPipeStalled: + return mojom::UsbTransferStatus::STALLED; + case kIOReturnOverrun: + return mojom::UsbTransferStatus::BABBLE; + case kIOReturnAborted: + return mojom::UsbTransferStatus::CANCELLED; + default: + return mojom::UsbTransferStatus::TRANSFER_ERROR; + } +} + +uint8_t ConvertTransferDirection(mojom::UsbTransferDirection direction) { + switch (direction) { + case mojom::UsbTransferDirection::INBOUND: + return static_cast<uint8_t>(EndpointDirection::kIn); + case mojom::UsbTransferDirection::OUTBOUND: + return static_cast<uint8_t>(EndpointDirection::kOut); + } + NOTREACHED(); + return 0; +} + +uint8_t CreateRequestType(mojom::UsbTransferDirection direction, + mojom::UsbControlTransferType request_type, + mojom::UsbControlTransferRecipient recipient) { + uint8_t result = ConvertTransferDirection(direction); + + switch (request_type) { + case mojom::UsbControlTransferType::STANDARD: + result |= static_cast<uint8_t>(RequestType::kStandard); + break; + case mojom::UsbControlTransferType::CLASS: + result |= static_cast<uint8_t>(RequestType::kClass); + break; + case mojom::UsbControlTransferType::VENDOR: + result |= static_cast<uint8_t>(RequestType::kVendor); + break; + case mojom::UsbControlTransferType::RESERVED: + result |= static_cast<uint8_t>(RequestType::kReserved); + break; + } + + switch (recipient) { + case mojom::UsbControlTransferRecipient::DEVICE: + result |= static_cast<uint8_t>(RequestRecipient::kDevice); + break; + case mojom::UsbControlTransferRecipient::INTERFACE: + result |= static_cast<uint8_t>(RequestRecipient::kInterface); + break; + case mojom::UsbControlTransferRecipient::ENDPOINT: + result |= static_cast<uint8_t>(RequestRecipient::kEndpoint); + break; + case mojom::UsbControlTransferRecipient::OTHER: + result |= static_cast<uint8_t>(RequestRecipient::kOther); + break; + } + + return result; +} + +} // namespace + +UsbDeviceHandleMac::UsbDeviceHandleMac( + scoped_refptr<UsbDeviceMac> device, + ScopedIOUSBDeviceInterface device_interface) + : device_interface_(std::move(device_interface)), + device_(std::move(device)) {} + +scoped_refptr<UsbDevice> UsbDeviceHandleMac::GetDevice() const { + return device_; +} + +void UsbDeviceHandleMac::Close() { + if (!device_) + return; + + IOReturn kr = (*device_interface_)->USBDeviceClose(device_interface_); + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to close device: " << std::hex << kr; + return; + } + + Clear(); + device_->HandleClosed(this); + device_ = nullptr; +} + +void UsbDeviceHandleMac::SetConfiguration(int configuration_value, + ResultCallback callback) { + if (!device_) { + std::move(callback).Run(false); + return; + } + + if (!base::IsValueInRangeForNumericType<uint8_t>(configuration_value)) { + std::move(callback).Run(false); + return; + } + + Clear(); + + IOReturn kr = + (*device_interface_) + ->SetConfiguration(device_interface_, + static_cast<uint8_t>(configuration_value)); + if (kr != kIOReturnSuccess) { + std::move(callback).Run(false); + return; + } + + device_->ActiveConfigurationChanged(configuration_value); + + std::move(callback).Run(true); +} + +void UsbDeviceHandleMac::ClaimInterface(int interface_number, + ResultCallback callback) { + if (!device_) { + std::move(callback).Run(false); + return; + } + + if (!base::IsValueInRangeForNumericType<uint8_t>(interface_number)) { + std::move(callback).Run(false); + return; + } + + IOUSBFindInterfaceRequest request; + request.bInterfaceClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; + request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; + request.bAlternateSetting = kIOUSBFindInterfaceDontCare; + + base::mac::ScopedIOObject<io_iterator_t> interface_iterator; + IOReturn kr = + (*device_interface_) + ->CreateInterfaceIterator(device_interface_, &request, + interface_iterator.InitializeInto()); + if (kr != kIOReturnSuccess) { + std::move(callback).Run(false); + return; + } + + base::mac::ScopedIOObject<io_service_t> usb_interface; + while (usb_interface.reset(IOIteratorNext(interface_iterator)), + usb_interface) { + base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> plugin_interface; + int32_t score; + kr = IOCreatePlugInInterfaceForService( + usb_interface, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, + plugin_interface.InitializeInto(), &score); + + if (kr != kIOReturnSuccess || !plugin_interface) { + USB_LOG(ERROR) << "Unable to create a plug-in: " << std::hex << kr; + continue; + } + + ScopedIOUSBInterfaceInterface interface_interface; + kr = (*plugin_interface) + ->QueryInterface(plugin_interface.get(), + CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), + reinterpret_cast<LPVOID*>( + interface_interface.InitializeInto())); + if (kr != kIOReturnSuccess || !interface_interface) { + USB_LOG(ERROR) << "Could not create a device interface: " << std::hex + << kr; + continue; + } + + uint8_t retrieved_interface_number; + kr = (*interface_interface) + ->GetInterfaceNumber(interface_interface, + &retrieved_interface_number); + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Could not retrieve an interface number: " << std::hex + << kr; + continue; + } + + if (retrieved_interface_number != interface_number) + continue; + + kr = (*interface_interface)->USBInterfaceOpen(interface_interface); + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Could not open interface: " << std::hex << kr; + break; + } + + interfaces_[interface_number] = interface_interface; + base::ScopedCFTypeRef<CFRunLoopSourceRef> run_loop_source; + kr = (*interface_interface) + ->CreateInterfaceAsyncEventSource( + interface_interface, run_loop_source.InitializeInto()); + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Could not retrieve port: " << std::hex << kr; + (*interface_interface)->USBInterfaceClose(interface_interface); + break; + } + RefreshEndpointMap(); + CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source.get(), + kCFRunLoopDefaultMode); + sources_[interface_number] = run_loop_source; + std::move(callback).Run(true); + return; + } + std::move(callback).Run(false); + USB_LOG(ERROR) << "Could not find interface matching number: " + << interface_number; +} + +void UsbDeviceHandleMac::ReleaseInterface(int interface_number, + ResultCallback callback) { + if (!device_) { + std::move(callback).Run(false); + return; + } + + auto interface_it = interfaces_.find(static_cast<uint8_t>(interface_number)); + if (interface_it == interfaces_.end()) { + std::move(callback).Run(false); + return; + } + + auto released_interface = std::move(interface_it->second); + interfaces_.erase(interface_it); + + auto source_it = sources_.find(interface_number); + if (source_it != sources_.end()) { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source_it->second.get(), + kCFRunLoopDefaultMode); + sources_.erase(source_it); + } + + IOReturn kr = (*released_interface)->USBInterfaceClose(released_interface); + if (kr != kIOReturnSuccess) { + std::move(callback).Run(false); + return; + } + RefreshEndpointMap(); + std::move(callback).Run(true); +} + +void UsbDeviceHandleMac::SetInterfaceAlternateSetting(int interface_number, + int alternate_setting, + ResultCallback callback) { + if (!device_) { + std::move(callback).Run(false); + return; + } + + auto interface_it = interfaces_.find(interface_number); + if (interface_it == interfaces_.end()) { + std::move(callback).Run(false); + return; + } + const auto& interface_interface = interface_it->second; + + IOReturn kr = + (*interface_interface) + ->SetAlternateInterface(interface_interface, alternate_setting); + if (kr != kIOReturnSuccess) { + std::move(callback).Run(false); + return; + } + RefreshEndpointMap(); + std::move(callback).Run(true); +} + +void UsbDeviceHandleMac::ResetDevice(ResultCallback callback) { + if (!device_) { + std::move(callback).Run(false); + return; + } + + // TODO(https://crbug.com/1096743): Figure out if open interfaces need to be + // closed as well. + IOReturn kr = (*device_interface_)->ResetDevice(device_interface_); + if (kr != kIOReturnSuccess) { + std::move(callback).Run(false); + return; + } + + Clear(); + std::move(callback).Run(true); +} + +void UsbDeviceHandleMac::ClearHalt(mojom::UsbTransferDirection direction, + uint8_t endpoint_number, + ResultCallback callback) { + if (!device_) { + std::move(callback).Run(false); + return; + } + + uint8_t endpoint_address = + ConvertTransferDirection(direction) | endpoint_number; + auto* mojom_interface = FindInterfaceByEndpoint(endpoint_address); + uint8_t interface_number = mojom_interface->interface_number; + + auto interface_it = interfaces_.find(interface_number); + if (interface_it == interfaces_.end()) { + std::move(callback).Run(false); + return; + } + + const auto endpoint_it = endpoint_map_.find(endpoint_address); + if (endpoint_it == endpoint_map_.end()) { + std::move(callback).Run(false); + return; + } + + const auto& interface_interface = interface_it->second; + IOReturn kr = (*interface_interface) + ->ClearPipeStall(interface_interface, + endpoint_it->second.pipe_reference); + if (kr != kIOReturnSuccess) { + std::move(callback).Run(false); + return; + } + + std::move(callback).Run(true); +} + +void UsbDeviceHandleMac::ControlTransfer( + mojom::UsbTransferDirection direction, + mojom::UsbControlTransferType request_type, + mojom::UsbControlTransferRecipient recipient, + uint8_t request, + uint16_t value, + uint16_t index, + scoped_refptr<base::RefCountedBytes> buffer, + unsigned int timeout, + TransferCallback callback) { + if (!device_) { + std::move(callback).Run(mojom::UsbTransferStatus::DISCONNECT, + std::move(buffer), 0); + return; + } + + if (!base::IsValueInRangeForNumericType<uint16_t>(buffer->size())) { + USB_LOG(ERROR) << "Transfer too long."; + std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR, + std::move(buffer), 0); + return; + } + + auto interface_it = interfaces_.find(index & 0xff); + if (interface_it == interfaces_.end()) { + std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR, + std::move(buffer), 0); + return; + } + + ScopedIOUSBInterfaceInterface interface = interface_it->second; + IOUSBDevRequestTO device_request; + device_request.bRequest = request; + device_request.wValue = value; + device_request.wIndex = index; + device_request.bmRequestType = + CreateRequestType(direction, request_type, recipient); + device_request.pData = buffer->front_as<void*>(); + device_request.wLength = static_cast<uint16_t>(buffer->size()); + device_request.completionTimeout = timeout; + device_request.noDataTimeout = timeout; + + auto transfer = std::make_unique<Transfer>(); + transfer->generic_callback = std::move(callback); + transfer->handle = this; + transfer->buffer = std::move(buffer); + + Transfer* transfer_ptr = transfer.get(); + auto result = transfers_.insert(std::move(transfer)); + IOReturn kr = + (*interface) + ->ControlRequestAsyncTO(interface, /*pipeRef=*/0, &device_request, + &AsyncIoCallback, + reinterpret_cast<void*>(transfer_ptr)); + + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to send control request: " << std::hex << kr; + std::move((*result.first)->generic_callback) + .Run(mojom::UsbTransferStatus::TRANSFER_ERROR, + std::move((*result.first)->buffer), 0); + transfers_.erase(result.first); + } +} + +void UsbDeviceHandleMac::IsochronousTransferIn( + uint8_t endpoint, + const std::vector<uint32_t>& packet_lengths, + unsigned int timeout, + IsochronousTransferCallback callback) { + if (!device_) { + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::DISCONNECT); + return; + } + + uint8_t endpoint_address = + ConvertTransferDirection(mojom::UsbTransferDirection::INBOUND) | endpoint; + const auto endpoint_it = endpoint_map_.find(endpoint_address); + if (endpoint_it == endpoint_map_.end()) { + USB_LOG(ERROR) << "Failed to submit transfer because endpoint " + << int{endpoint_address} + << " is not part of a claimed interface."; + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + return; + } + + size_t length = + std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u); + auto buffer = base::MakeRefCounted<base::RefCountedBytes>(length); + + auto interface_it = + interfaces_.find(endpoint_it->second.interface->interface_number); + if (interface_it == interfaces_.end()) { + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + return; + } + const auto& interface_interface = interface_it->second; + + uint64_t bus_frame; + AbsoluteTime time; + IOReturn kr = (*interface_interface) + ->GetBusFrameNumber(interface_interface, &bus_frame, &time); + if (kr != kIOReturnSuccess) { + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + return; + } + + auto transfer = std::make_unique<Transfer>(); + transfer->isochronous_callback = std::move(callback); + transfer->handle = this; + transfer->buffer = buffer; + transfer->type = mojom::UsbTransferType::ISOCHRONOUS; + + Transfer* transfer_data = transfer.get(); + auto result = transfers_.insert(std::move(transfer)); + + std::vector<IOUSBIsocFrame> frame_list; + for (const auto& size : packet_lengths) { + if (!base::IsValueInRangeForNumericType<uint16_t>(size)) { + USB_LOG(ERROR) << "Transfer too long."; + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + return; + } + IOUSBIsocFrame frame_entry; + frame_entry.frReqCount = static_cast<uint16_t>(size); + frame_list.push_back(frame_entry); + } + transfer->frame_list = frame_list; + + kr = (*interface_interface) + ->ReadIsochPipeAsync(interface_interface, + endpoint_it->second.pipe_reference, + buffer->front_as<void*>(), bus_frame, + static_cast<uint32_t>(packet_lengths.size()), + transfer->frame_list.data(), &AsyncIoCallback, + reinterpret_cast<void*>(transfer_data)); + + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Isochrnous read failed."; + ReportIsochronousTransferError( + std::move((*result.first)->isochronous_callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + transfers_.erase(result.first); + return; + } +} + +void UsbDeviceHandleMac::IsochronousTransferOut( + uint8_t endpoint, + scoped_refptr<base::RefCountedBytes> buffer, + const std::vector<uint32_t>& packet_lengths, + unsigned int timeout, + IsochronousTransferCallback callback) { + if (!device_) { + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::DISCONNECT); + return; + } + + uint8_t endpoint_address = + ConvertTransferDirection(mojom::UsbTransferDirection::INBOUND) | endpoint; + const auto endpoint_it = endpoint_map_.find(endpoint_address); + if (endpoint_it == endpoint_map_.end()) { + USB_LOG(ERROR) << "Failed to submit transfer because endpoint " + << int{endpoint_address} + << " is not part of a claimed interface."; + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + return; + } + + auto interface_it = + interfaces_.find(endpoint_it->second.interface->interface_number); + if (interface_it == interfaces_.end()) { + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + return; + } + const auto& interface_interface = interface_it->second; + + uint64_t bus_frame; + AbsoluteTime time; + IOReturn kr = (*interface_interface) + ->GetBusFrameNumber(interface_interface, &bus_frame, &time); + if (kr != kIOReturnSuccess) { + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + return; + } + + auto transfer = std::make_unique<Transfer>(); + transfer->isochronous_callback = std::move(callback); + transfer->handle = this; + transfer->buffer = buffer; + transfer->type = mojom::UsbTransferType::ISOCHRONOUS; + + Transfer* transfer_data = transfer.get(); + auto result = transfers_.insert(std::move(transfer)); + + std::vector<IOUSBIsocFrame> frame_list; + for (const auto& size : packet_lengths) { + if (!base::IsValueInRangeForNumericType<uint16_t>(size)) { + USB_LOG(ERROR) << "Transfer too long."; + ReportIsochronousTransferError(std::move(callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + return; + } + IOUSBIsocFrame frame_entry; + frame_entry.frReqCount = static_cast<uint16_t>(size); + frame_list.push_back(frame_entry); + } + transfer->frame_list = frame_list; + + kr = (*interface_interface) + ->WriteIsochPipeAsync(interface_interface, + endpoint_it->second.pipe_reference, + buffer->front_as<void*>(), bus_frame, + static_cast<uint32_t>(packet_lengths.size()), + transfer->frame_list.data(), &AsyncIoCallback, + reinterpret_cast<void*>(transfer_data)); + + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Isochrnous write failed."; + ReportIsochronousTransferError( + std::move((*result.first)->isochronous_callback), packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + transfers_.erase(result.first); + } +} + +void UsbDeviceHandleMac::GenericTransfer( + mojom::UsbTransferDirection direction, + uint8_t endpoint_number, + scoped_refptr<base::RefCountedBytes> buffer, + unsigned int timeout, + TransferCallback callback) { + if (!device_) { + std::move(callback).Run(mojom::UsbTransferStatus::DISCONNECT, buffer, 0); + return; + } + + uint8_t endpoint_address = + ConvertEndpointNumberToAddress(endpoint_number, direction); + + const auto endpoint_it = endpoint_map_.find(endpoint_address); + if (endpoint_it == endpoint_map_.end()) { + USB_LOG(ERROR) << "Failed to submit transfer because endpoint " + << int{endpoint_address} + << " is not part of a claimed interface."; + std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR, buffer, + 0); + return; + } + + if (!base::IsValueInRangeForNumericType<uint32_t>(buffer->size())) { + USB_LOG(ERROR) << "Transfer too long."; + std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR, buffer, + 0); + return; + } + + auto interface_it = + interfaces_.find(endpoint_it->second.interface->interface_number); + if (interface_it == interfaces_.end()) { + std::move(callback).Run(mojom::UsbTransferStatus::TRANSFER_ERROR, buffer, + 0); + return; + } + const auto& interface_interface = interface_it->second; + + auto transfer = std::make_unique<Transfer>(); + transfer->generic_callback = std::move(callback); + transfer->handle = this; + transfer->buffer = buffer; + + mojom::UsbTransferType transfer_type = endpoint_it->second.endpoint->type; + transfer->type = transfer_type; + + switch (transfer_type) { + case mojom::UsbTransferType::BULK: + switch (direction) { + case mojom::UsbTransferDirection::INBOUND: + BulkIn(std::move(interface_interface), + endpoint_it->second.pipe_reference, buffer, + static_cast<uint32_t>(timeout), std::move(transfer)); + return; + case mojom::UsbTransferDirection::OUTBOUND: + BulkOut(std::move(interface_interface), + endpoint_it->second.pipe_reference, buffer, + static_cast<uint32_t>(timeout), std::move(transfer)); + return; + } + case mojom::UsbTransferType::INTERRUPT: + switch (direction) { + case mojom::UsbTransferDirection::INBOUND: + InterruptIn(interface_interface, endpoint_it->second.pipe_reference, + buffer, std::move(transfer)); + return; + case mojom::UsbTransferDirection::OUTBOUND: + InterruptOut(interface_interface, endpoint_it->second.pipe_reference, + buffer, std::move(transfer)); + return; + } + default: + std::move(transfer->generic_callback) + .Run(mojom::UsbTransferStatus::TRANSFER_ERROR, buffer, 0); + } +} + +const mojom::UsbInterfaceInfo* UsbDeviceHandleMac::FindInterfaceByEndpoint( + uint8_t endpoint_address) { + const auto endpoint_it = endpoint_map_.find(endpoint_address); + if (endpoint_it != endpoint_map_.end()) + return endpoint_it->second.interface; + return nullptr; +} + +UsbDeviceHandleMac::~UsbDeviceHandleMac() {} + +void UsbDeviceHandleMac::BulkIn( + const ScopedIOUSBInterfaceInterface& interface_interface, + uint8_t pipe_reference, + scoped_refptr<base::RefCountedBytes> buffer, + uint32_t timeout, + std::unique_ptr<Transfer> transfer) { + Transfer* transfer_data = transfer.get(); + auto result = transfers_.insert(std::move(transfer)); + IOReturn kr = (*interface_interface) + ->ReadPipeAsyncTO(interface_interface, pipe_reference, + buffer->front_as<void*>(), + static_cast<uint32_t>(buffer->size()), + timeout, timeout, &AsyncIoCallback, + reinterpret_cast<void*>(transfer_data)); + + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to read from device: " << std::hex << kr; + std::move((*result.first)->generic_callback) + .Run(mojom::UsbTransferStatus::TRANSFER_ERROR, buffer, 0); + transfers_.erase(result.first); + } +} + +void UsbDeviceHandleMac::BulkOut( + const ScopedIOUSBInterfaceInterface& interface_interface, + uint8_t pipe_reference, + scoped_refptr<base::RefCountedBytes> buffer, + uint32_t timeout, + std::unique_ptr<Transfer> transfer) { + Transfer* transfer_data = transfer.get(); + auto result = transfers_.insert(std::move(transfer)); + IOReturn kr = (*interface_interface) + ->WritePipeAsyncTO(interface_interface, pipe_reference, + buffer->front_as<void*>(), + static_cast<uint32_t>(buffer->size()), + timeout, timeout, &AsyncIoCallback, + reinterpret_cast<void*>(transfer_data)); + + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to write to device: " << std::hex << kr; + std::move((*result.first)->generic_callback) + .Run(mojom::UsbTransferStatus::TRANSFER_ERROR, buffer, 0); + transfers_.erase(result.first); + } +} + +void UsbDeviceHandleMac::InterruptIn( + const ScopedIOUSBInterfaceInterface& interface_interface, + uint8_t pipe_reference, + scoped_refptr<base::RefCountedBytes> buffer, + std::unique_ptr<Transfer> transfer) { + Transfer* transfer_data = transfer.get(); + auto result = transfers_.insert(std::move(transfer)); + IOReturn kr = (*interface_interface) + ->ReadPipeAsync(interface_interface, pipe_reference, + buffer->front_as<void*>(), + static_cast<uint32_t>(buffer->size()), + &AsyncIoCallback, + reinterpret_cast<void*>(transfer_data)); + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to read from device: " << std::hex << kr; + std::move(transfer->generic_callback) + .Run(mojom::UsbTransferStatus::TRANSFER_ERROR, buffer, 0); + transfers_.erase(result.first); + } +} + +void UsbDeviceHandleMac::InterruptOut( + const ScopedIOUSBInterfaceInterface& interface_interface, + uint8_t pipe_reference, + scoped_refptr<base::RefCountedBytes> buffer, + std::unique_ptr<Transfer> transfer) { + Transfer* transfer_data = transfer.get(); + auto result = transfers_.insert(std::move(transfer)); + IOReturn kr = (*interface_interface) + ->WritePipeAsync(interface_interface, pipe_reference, + buffer->front_as<void*>(), + static_cast<uint32_t>(buffer->size()), + &AsyncIoCallback, + reinterpret_cast<void*>(transfer_data)); + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to write to device: " << std::hex << kr; + std::move(transfer->generic_callback) + .Run(mojom::UsbTransferStatus::TRANSFER_ERROR, buffer, 0); + transfers_.erase(result.first); + } +} + +void UsbDeviceHandleMac::RefreshEndpointMap() { + endpoint_map_.clear(); + const mojom::UsbConfigurationInfo* config = device_->GetActiveConfiguration(); + if (!config) + return; + + for (const auto& map_entry : interfaces_) { + uint8_t alternate_setting; + IOReturn kr = + (*map_entry.second) + ->GetAlternateSetting(map_entry.second, &alternate_setting); + if (kr != kIOReturnSuccess) + continue; + CombinedInterfaceInfo interface_info = + FindInterfaceInfoFromConfig(config, map_entry.first, alternate_setting); + + if (!interface_info.IsValid()) + continue; + + // macOS references an interface's endpoint via an index number of the + // endpoint we want in the given interface. It is called a pipe reference. + // The indices start at 1 for each interface. + uint8_t pipe_reference = 1; + for (const auto& endpoint : interface_info.alternate->endpoints) { + endpoint_map_[ConvertEndpointNumberToAddress(*endpoint)] = { + interface_info.interface, endpoint.get(), pipe_reference}; + pipe_reference++; + } + } +} + +void UsbDeviceHandleMac::ReportIsochronousTransferError( + UsbDeviceHandle::IsochronousTransferCallback callback, + std::vector<uint32_t> packet_lengths, + mojom::UsbTransferStatus status) { + std::vector<mojom::UsbIsochronousPacketPtr> packets; + packets.reserve(packet_lengths.size()); + for (const auto& packet_length : packet_lengths) { + auto packet = mojom::UsbIsochronousPacket::New(); + packet->length = packet_length; + packet->transferred_length = 0; + packet->status = status; + packets.push_back(std::move(packet)); + } + std::move(callback).Run(nullptr, std::move(packets)); +} + +void UsbDeviceHandleMac::Clear() { + base::flat_set<std::unique_ptr<Transfer>, base::UniquePtrComparator> + transfers; + transfers.swap(transfers_); + for (auto& transfer : transfers) { + DCHECK(transfer); + if (transfer->type == mojom::UsbTransferType::ISOCHRONOUS) { + ReportIsochronousTransferError(std::move(transfer->isochronous_callback), + transfer->packet_lengths, + mojom::UsbTransferStatus::TRANSFER_ERROR); + } else { + std::move(transfer->generic_callback) + .Run(mojom::UsbTransferStatus::TRANSFER_ERROR, + std::move(transfer->buffer), 0); + } + } + transfers.clear(); + interfaces_.clear(); + sources_.clear(); +} + +void UsbDeviceHandleMac::OnAsyncGeneric(IOReturn result, + size_t size, + Transfer* transfer) { + auto transfer_it = transfers_.find(transfer); + if (transfer_it == transfers_.end()) + return; + auto transfer_ptr = std::move(*transfer_it); + + std::move(transfer_ptr->generic_callback) + .Run(mojom::UsbTransferStatus::COMPLETED, transfer_ptr->buffer, + transfer_ptr->buffer->size()); + transfers_.erase(transfer_it); +} + +void UsbDeviceHandleMac::OnAsyncIsochronous(IOReturn result, + size_t size, + Transfer* transfer) { + auto transfer_it = transfers_.find(transfer); + if (transfer_it == transfers_.end()) + return; + auto transfer_ptr = std::move(*transfer_it); + + std::vector<mojom::UsbIsochronousPacketPtr> packets; + packets.reserve(transfer_ptr->frame_list.size()); + for (const auto& frame : transfer_ptr->frame_list) { + auto packet = mojom::UsbIsochronousPacket::New(); + packet->length = frame.frReqCount; + packet->transferred_length = frame.frActCount; + packet->status = ConvertTransferStatus(frame.frStatus); + packets.push_back(std::move(packet)); + } + + std::move(transfer_ptr->isochronous_callback) + .Run(transfer_ptr->buffer, std::move(packets)); + transfers_.erase(transfer_it); +} + +// static +void UsbDeviceHandleMac::AsyncIoCallback(void* refcon, + IOReturn result, + void* arg0) { + auto* transfer = reinterpret_cast<Transfer*>(refcon); + DCHECK(transfer); + DCHECK(transfer->handle); + if (transfer->type == mojom::UsbTransferType::ISOCHRONOUS) { + transfer->handle->OnAsyncIsochronous(result, reinterpret_cast<size_t>(arg0), + transfer); + return; + } + transfer->handle->OnAsyncGeneric(result, reinterpret_cast<size_t>(arg0), + transfer); +} + +} // namespace device diff --git a/chromium/services/device/usb/usb_device_handle_mac.h b/chromium/services/device/usb/usb_device_handle_mac.h new file mode 100644 index 00000000000..a8fbd4c3a47 --- /dev/null +++ b/chromium/services/device/usb/usb_device_handle_mac.h @@ -0,0 +1,146 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_DEVICE_USB_USB_DEVICE_HANDLE_MAC_H_ +#define SERVICES_DEVICE_USB_USB_DEVICE_HANDLE_MAC_H_ + +#include "services/device/usb/usb_device_handle.h" + +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOReturn.h> +#include <IOKit/usb/IOUSBLib.h> + +#include <memory> +#include <vector> + +#include "base/containers/flat_set.h" +#include "base/containers/unique_ptr_adapters.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/mac/scoped_ioplugininterface.h" +#include "services/device/public/mojom/usb_device.mojom.h" + +namespace base { +class RefCountedBytes; +} + +namespace device { + +class UsbDeviceMac; +struct Transfer; + +class UsbDeviceHandleMac : public UsbDeviceHandle { + public: + using ScopedIOUSBDeviceInterface = + base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface182>; + using ScopedIOUSBInterfaceInterface = + base::mac::ScopedIOPluginInterface<IOUSBInterfaceInterface182>; + + // UsbDeviceHandle implementation: + UsbDeviceHandleMac(scoped_refptr<UsbDeviceMac> device, + ScopedIOUSBDeviceInterface device_interface); + UsbDeviceHandleMac(const UsbDeviceHandleMac&) = delete; + UsbDeviceHandleMac& operator=(const UsbDeviceHandleMac&) = delete; + scoped_refptr<UsbDevice> GetDevice() const override; + void Close() override; + void SetConfiguration(int configuration_value, + ResultCallback callback) override; + void ClaimInterface(int interface_number, ResultCallback callback) override; + void ReleaseInterface(int interface_number, ResultCallback callback) override; + void SetInterfaceAlternateSetting(int interface_number, + int alternate_setting, + ResultCallback callback) override; + void ResetDevice(ResultCallback callback) override; + void ClearHalt(mojom::UsbTransferDirection direction, + uint8_t endpoint_number, + ResultCallback callback) override; + void ControlTransfer(mojom::UsbTransferDirection direction, + mojom::UsbControlTransferType request_type, + mojom::UsbControlTransferRecipient recipient, + uint8_t request, + uint16_t value, + uint16_t index, + scoped_refptr<base::RefCountedBytes> buffer, + unsigned int timeout, + TransferCallback callback) override; + void IsochronousTransferIn(uint8_t endpoint, + const std::vector<uint32_t>& packet_lengths, + unsigned int timeout, + IsochronousTransferCallback callback) override; + void IsochronousTransferOut(uint8_t endpoint, + scoped_refptr<base::RefCountedBytes> buffer, + const std::vector<uint32_t>& packet_lengths, + unsigned int timeout, + IsochronousTransferCallback callback) override; + void GenericTransfer(mojom::UsbTransferDirection direction, + uint8_t endpoint_number, + scoped_refptr<base::RefCountedBytes> buffer, + unsigned int timeout, + TransferCallback callback) override; + const mojom::UsbInterfaceInfo* FindInterfaceByEndpoint( + uint8_t endpoint_address) override; + + protected: + ~UsbDeviceHandleMac() override; + + private: + struct EndpointMapValue { + const mojom::UsbInterfaceInfo* interface; + const mojom::UsbEndpointInfo* endpoint; + uint8_t pipe_reference; + }; + + void BulkIn(const ScopedIOUSBInterfaceInterface& interface_interface, + uint8_t pipe_reference, + scoped_refptr<base::RefCountedBytes> buffer, + uint32_t timeout, + std::unique_ptr<Transfer> transfer); + void BulkOut(const ScopedIOUSBInterfaceInterface& interface_interface, + uint8_t pipe_reference, + scoped_refptr<base::RefCountedBytes> buffer, + uint32_t timeout, + std::unique_ptr<Transfer> transfer); + void InterruptIn(const ScopedIOUSBInterfaceInterface& interface_interface, + uint8_t pipe_reference, + scoped_refptr<base::RefCountedBytes> buffer, + std::unique_ptr<Transfer> transfer); + void InterruptOut(const ScopedIOUSBInterfaceInterface& interface_interface, + uint8_t pipe_reference, + scoped_refptr<base::RefCountedBytes> buffer, + std::unique_ptr<Transfer> transfer); + // Refresh endpoint_map_ after ClaimInterface, ReleaseInterface and + // SetInterfaceAlternateSetting. It is needed so that endpoints can be mapped + // to their respective mojom Interface. + void RefreshEndpointMap(); + + void ReportIsochronousTransferError( + UsbDeviceHandle::IsochronousTransferCallback callback, + std::vector<uint32_t> packet_lengths, + mojom::UsbTransferStatus status); + + void Clear(); + + void OnAsyncGeneric(IOReturn result, size_t size, Transfer* transfer); + void OnAsyncIsochronous(IOReturn result, size_t size, Transfer* transfer); + static void AsyncIoCallback(void* refcon, IOReturn result, void* arg0); + + // A map from the endpoint indices to its corresponding EndpointMapValue, + // which contains the Interface and Endpoint Mojo structures. + using EndpointMap = base::flat_map<int, EndpointMapValue>; + EndpointMap endpoint_map_; + + base::flat_set<std::unique_ptr<Transfer>, base::UniquePtrComparator> + transfers_; + + ScopedIOUSBDeviceInterface device_interface_; + scoped_refptr<UsbDeviceMac> device_; + + // Both maps take the interface number in as the respective key. + base::flat_map<uint8_t, ScopedIOUSBInterfaceInterface> interfaces_; + base::flat_map<uint8_t, base::ScopedCFTypeRef<CFRunLoopSourceRef>> sources_; +}; + +} // namespace device + +#endif // SERVICES_DEVICE_USB_USB_DEVICE_HANDLE_MAC_H_ diff --git a/chromium/services/device/usb/usb_device_handle_win.cc b/chromium/services/device/usb/usb_device_handle_win.cc index e9364245b16..1d94b7dc08c 100644 --- a/chromium/services/device/usb/usb_device_handle_win.cc +++ b/chromium/services/device/usb/usb_device_handle_win.cc @@ -46,7 +46,7 @@ using mojom::UsbTransferStatus; namespace { -const base::StringPiece16 kWinUsbDriverName = L"winusb"; +const base::WStringPiece kWinUsbDriverName = L"winusb"; uint8_t BuildRequestFlags(UsbTransferDirection direction, UsbControlTransferType request_type, @@ -618,8 +618,8 @@ UsbDeviceHandleWin::~UsbDeviceHandleWin() { } void UsbDeviceHandleWin::UpdateFunction(int interface_number, - const base::string16& function_driver, - const base::string16& function_path) { + const std::wstring& function_driver, + const std::wstring& function_path) { auto it = interfaces_.find(interface_number); if (it == interfaces_.end()) return; diff --git a/chromium/services/device/usb/usb_device_handle_win.h b/chromium/services/device/usb/usb_device_handle_win.h index c0a736b67d3..de6e1459697 100644 --- a/chromium/services/device/usb/usb_device_handle_win.h +++ b/chromium/services/device/usb/usb_device_handle_win.h @@ -86,8 +86,8 @@ class UsbDeviceHandleWin : public UsbDeviceHandle { ~UsbDeviceHandleWin() override; void UpdateFunction(int interface_number, - const base::string16& function_driver, - const base::string16& function_path); + const std::wstring& function_driver, + const std::wstring& function_path); private: struct Interface; @@ -102,8 +102,8 @@ class UsbDeviceHandleWin : public UsbDeviceHandle { const mojom::UsbInterfaceInfo* info; // In a composite device each function has its own driver and path to open. - base::string16 function_driver; - base::string16 function_path; + std::wstring function_driver; + std::wstring function_path; base::win::ScopedHandle function_handle; ScopedWinUsbHandle handle; diff --git a/chromium/services/device/usb/usb_device_impl.cc b/chromium/services/device/usb/usb_device_impl.cc index ed259413c9f..94f2d9c8cf4 100644 --- a/chromium/services/device/usb/usb_device_impl.cc +++ b/chromium/services/device/usb/usb_device_impl.cc @@ -78,7 +78,7 @@ void UsbDeviceImpl::ReadAllConfigurations() { continue; } - if (!usb_descriptor.Parse(std::vector<uint8_t>(buffer, buffer + rv))) + if (!usb_descriptor.Parse(base::make_span(buffer, rv))) USB_LOG(EVENT) << "Config descriptor index " << i << " was corrupt."; free(buffer); diff --git a/chromium/services/device/usb/usb_device_mac.cc b/chromium/services/device/usb/usb_device_mac.cc new file mode 100644 index 00000000000..d67e566d1ea --- /dev/null +++ b/chromium/services/device/usb/usb_device_mac.cc @@ -0,0 +1,87 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/usb/usb_device_mac.h" + +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOReturn.h> +#include <IOKit/usb/IOUSBLib.h> + +#include <utility> + +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_ionotificationportref.h" +#include "base/mac/scoped_ioobject.h" +#include "base/mac/scoped_ioplugininterface.h" +#include "components/device_event_log/device_event_log.h" +#include "services/device/usb/usb_descriptors.h" +#include "services/device/usb/usb_device_handle.h" +#include "services/device/usb/usb_device_handle_mac.h" + +namespace device { + +UsbDeviceMac::UsbDeviceMac(uint64_t entry_id, + mojom::UsbDeviceInfoPtr device_info) + : UsbDevice(std::move(device_info)), entry_id_(entry_id) {} + +UsbDeviceMac::~UsbDeviceMac() = default; + +void UsbDeviceMac::Open(OpenCallback callback) { + base::ScopedCFTypeRef<CFDictionaryRef> matching_dict( + IORegistryEntryIDMatching(entry_id())); + if (!matching_dict.get()) { + USB_LOG(ERROR) << "Failed to create matching dictionary for ID."; + std::move(callback).Run(nullptr); + return; + } + // IOServiceGetMatchingService consumes a reference to the matching dictionary + // passed to it. + base::mac::ScopedIOObject<io_service_t> usb_device( + IOServiceGetMatchingService(kIOMasterPortDefault, + matching_dict.release())); + if (!usb_device.get()) { + USB_LOG(ERROR) << "IOService not found for ID."; + std::move(callback).Run(nullptr); + return; + } + + base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> plugin_interface; + int32_t score; + IOReturn kr = IOCreatePlugInInterfaceForService( + usb_device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, + plugin_interface.InitializeInto(), &score); + if ((kr != kIOReturnSuccess) || !plugin_interface) { + USB_LOG(ERROR) << "Unable to create a plug-in: " << std::hex << kr; + std::move(callback).Run(nullptr); + return; + } + + base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface182> device_interface; + kr = (*plugin_interface) + ->QueryInterface( + plugin_interface.get(), + CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), + reinterpret_cast<LPVOID*>(device_interface.InitializeInto())); + if (kr != kIOReturnSuccess || !device_interface) { + USB_LOG(ERROR) << "Couldn’t create a device interface."; + std::move(callback).Run(nullptr); + return; + } + + kr = (*device_interface)->USBDeviceOpen(device_interface); + if (kr != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to open device: " << std::hex << kr; + std::move(callback).Run(nullptr); + return; + } + + auto device_handle = base::MakeRefCounted<UsbDeviceHandleMac>( + this, std::move(device_interface)); + + handles().push_back(device_handle.get()); + std::move(callback).Run(device_handle); +} + +} // namespace device diff --git a/chromium/services/device/usb/usb_device_mac.h b/chromium/services/device/usb/usb_device_mac.h new file mode 100644 index 00000000000..f27b56b1ac3 --- /dev/null +++ b/chromium/services/device/usb/usb_device_mac.h @@ -0,0 +1,32 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_DEVICE_USB_USB_DEVICE_MAC_H_ +#define SERVICES_DEVICE_USB_USB_DEVICE_MAC_H_ + +#include "services/device/usb/usb_device.h" + +namespace device { + +class UsbDeviceMac : public UsbDevice { + public: + UsbDeviceMac(uint64_t entry_id, mojom::UsbDeviceInfoPtr device_info); + UsbDeviceMac(const UsbDeviceMac&) = delete; + UsbDeviceMac& operator=(const UsbDeviceMac&) = delete; + + // UsbDevice implementation: + void Open(OpenCallback callback) override; + + uint64_t entry_id() const { return entry_id_; } + + protected: + ~UsbDeviceMac() override; + + private: + const uint64_t entry_id_; +}; + +} // namespace device + +#endif // SERVICES_DEVICE_USB_USB_DEVICE_MAC_H_ diff --git a/chromium/services/device/usb/usb_device_win.cc b/chromium/services/device/usb/usb_device_win.cc index 940b0ee2ad8..f5303bf93b7 100644 --- a/chromium/services/device/usb/usb_device_win.cc +++ b/chromium/services/device/usb/usb_device_win.cc @@ -24,12 +24,12 @@ namespace { const uint16_t kUsbVersion2_1 = 0x0210; } // namespace -UsbDeviceWin::UsbDeviceWin(const base::string16& device_path, - const base::string16& hub_path, +UsbDeviceWin::UsbDeviceWin(const std::wstring& device_path, + const std::wstring& hub_path, const base::flat_map<int, FunctionInfo>& functions, uint32_t bus_number, uint32_t port_number, - const base::string16& driver_name) + const std::wstring& driver_name) : UsbDevice(bus_number, port_number), device_path_(device_path), hub_path_(hub_path), @@ -111,11 +111,11 @@ void UsbDeviceWin::OnReadDescriptors( auto string_map = std::make_unique<std::map<uint8_t, base::string16>>(); if (descriptor->i_manufacturer) - (*string_map)[descriptor->i_manufacturer] = base::string16(); + (*string_map)[descriptor->i_manufacturer]; if (descriptor->i_product) - (*string_map)[descriptor->i_product] = base::string16(); + (*string_map)[descriptor->i_product]; if (descriptor->i_serial_number) - (*string_map)[descriptor->i_serial_number] = base::string16(); + (*string_map)[descriptor->i_serial_number]; ReadUsbStringDescriptors( device_handle, std::move(string_map), diff --git a/chromium/services/device/usb/usb_device_win.h b/chromium/services/device/usb/usb_device_win.h index 9a6198128b2..11fcdf533ee 100644 --- a/chromium/services/device/usb/usb_device_win.h +++ b/chromium/services/device/usb/usb_device_win.h @@ -23,16 +23,16 @@ struct WebUsbPlatformCapabilityDescriptor; class UsbDeviceWin : public UsbDevice { public: struct FunctionInfo { - base::string16 driver; - base::string16 path; + std::wstring driver; + std::wstring path; }; - UsbDeviceWin(const base::string16& device_path, - const base::string16& hub_path, + UsbDeviceWin(const std::wstring& device_path, + const std::wstring& hub_path, const base::flat_map<int, FunctionInfo>& functions, uint32_t bus_number, uint32_t port_number, - const base::string16& driver_name); + const std::wstring& driver_name); // UsbDevice implementation: void Open(OpenCallback callback) override; @@ -43,11 +43,11 @@ class UsbDeviceWin : public UsbDevice { ~UsbDeviceWin() override; - const base::string16& device_path() const { return device_path_; } + const std::wstring& device_path() const { return device_path_; } const base::flat_map<int, FunctionInfo>& functions() const { return functions_; } - const base::string16& driver_name() const { return driver_name_; } + const std::wstring& driver_name() const { return driver_name_; } // Opens the device's parent hub in order to read the device, configuration // and string descriptors. @@ -82,10 +82,10 @@ class UsbDeviceWin : public UsbDevice { private: SEQUENCE_CHECKER(sequence_checker_); - const base::string16 device_path_; - const base::string16 hub_path_; + const std::wstring device_path_; + const std::wstring hub_path_; base::flat_map<int, FunctionInfo> functions_; - const base::string16 driver_name_; + const std::wstring driver_name_; DISALLOW_COPY_AND_ASSIGN(UsbDeviceWin); }; diff --git a/chromium/services/device/usb/usb_interface_android.cc b/chromium/services/device/usb/usb_interface_android.cc index decb143deda..707f0585627 100644 --- a/chromium/services/device/usb/usb_interface_android.cc +++ b/chromium/services/device/usb/usb_interface_android.cc @@ -4,7 +4,6 @@ #include "services/device/usb/usb_interface_android.h" -#include "base/android/build_info.h" #include "services/device/usb/jni_headers/ChromeUsbInterface_jni.h" #include "services/device/usb/usb_endpoint_android.h" @@ -19,12 +18,8 @@ mojom::UsbInterfaceInfoPtr UsbInterfaceAndroid::Convert( ScopedJavaLocalRef<jobject> wrapper = Java_ChromeUsbInterface_create(env, usb_interface); - uint8_t alternate_setting = 0; - if (base::android::BuildInfo::GetInstance()->sdk_int() >= - base::android::SDK_VERSION_LOLLIPOP) { - alternate_setting = - Java_ChromeUsbInterface_getAlternateSetting(env, wrapper); - } + uint8_t alternate_setting = + Java_ChromeUsbInterface_getAlternateSetting(env, wrapper); auto interface = BuildUsbInterfaceInfoPtr( Java_ChromeUsbInterface_getInterfaceNumber(env, wrapper), diff --git a/chromium/services/device/usb/usb_service.cc b/chromium/services/device/usb/usb_service.cc index b41322ff990..94be02cc9dd 100644 --- a/chromium/services/device/usb/usb_service.cc +++ b/chromium/services/device/usb/usb_service.cc @@ -4,6 +4,8 @@ #include "services/device/usb/usb_service.h" +#include <utility> + #include "base/bind.h" #include "base/feature_list.h" #include "base/location.h" @@ -21,7 +23,9 @@ #elif defined(USE_UDEV) #include "services/device/usb/usb_service_linux.h" #else -#if defined(OS_WIN) +#if defined(OS_MAC) +#include "services/device/usb/usb_service_mac.h" +#elif defined(OS_WIN) #include "services/device/usb/usb_service_win.h" #endif #include "services/device/usb/usb_service_impl.h" @@ -54,8 +58,11 @@ std::unique_ptr<UsbService> UsbService::Create() { return base::WrapUnique(new UsbServiceWin()); else return base::WrapUnique(new UsbServiceImpl()); -#elif defined(OS_MACOSX) - return base::WrapUnique(new UsbServiceImpl()); +#elif defined(OS_MAC) + if (base::FeatureList::IsEnabled(kNewUsbBackend)) + return base::WrapUnique(new UsbServiceMac()); + else + return base::WrapUnique(new UsbServiceImpl()); #else return nullptr; #endif diff --git a/chromium/services/device/usb/usb_service_impl.cc b/chromium/services/device/usb/usb_service_impl.cc index 3cb6beea47a..759a3f19dc9 100644 --- a/chromium/services/device/usb/usb_service_impl.cc +++ b/chromium/services/device/usb/usb_service_impl.cc @@ -50,7 +50,7 @@ const uint16_t kUsbVersion2_1 = 0x0210; #if defined(OS_WIN) -bool IsWinUsbInterface(const base::string16& device_path) { +bool IsWinUsbInterface(const std::wstring& device_path) { DeviceInfoQueryWin device_info_query; if (!device_info_query.device_info_list_valid()) { USB_PLOG(ERROR) << "Failed to create a device information set"; @@ -97,7 +97,7 @@ scoped_refptr<UsbContext> InitializeUsbContextBlocking() { } base::Optional<std::vector<ScopedLibusbDeviceRef>> GetDeviceListBlocking( - const base::string16& new_device_path, + const std::wstring& new_device_path, scoped_refptr<UsbContext> usb_context) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); @@ -267,20 +267,20 @@ void UsbServiceImpl::GetDevices(GetDevicesCallback callback) { #if defined(OS_WIN) void UsbServiceImpl::OnDeviceAdded(const GUID& class_guid, - const base::string16& device_path) { + const std::wstring& device_path) { // Only the root node of a composite USB device has the class GUID // GUID_DEVINTERFACE_USB_DEVICE but we want to wait until WinUSB is loaded. // This first pass filter will catch anything that's sitting on the USB bus // (including devices on 3rd party USB controllers) to avoid the more // expensive driver check that needs to be done on the FILE thread. - if (device_path.find(L"usb") != base::string16::npos) { + if (device_path.find(L"usb") != std::wstring::npos) { pending_path_enumerations_.push(device_path); RefreshDevices(); } } void UsbServiceImpl::OnDeviceRemoved(const GUID& class_guid, - const base::string16& device_path) { + const std::wstring& device_path) { // The root USB device node is removed last. if (class_guid == GUID_DEVINTERFACE_USB_DEVICE) { RefreshDevices(); @@ -326,7 +326,7 @@ void UsbServiceImpl::RefreshDevices() { enumeration_in_progress_ = true; DCHECK(devices_being_enumerated_.empty()); - base::string16 device_path; + std::wstring device_path; if (!pending_path_enumerations_.empty()) { device_path = pending_path_enumerations_.front(); pending_path_enumerations_.pop(); diff --git a/chromium/services/device/usb/usb_service_impl.h b/chromium/services/device/usb/usb_service_impl.h index 669f8576f24..32a2efb106b 100644 --- a/chromium/services/device/usb/usb_service_impl.h +++ b/chromium/services/device/usb/usb_service_impl.h @@ -53,9 +53,9 @@ class UsbServiceImpl final : #if defined(OS_WIN) // device::DeviceMonitorWin::Observer implementation void OnDeviceAdded(const GUID& class_guid, - const base::string16& device_path) override; + const std::wstring& device_path) override; void OnDeviceRemoved(const GUID& class_guid, - const base::string16& device_path) override; + const std::wstring& device_path) override; #endif // OS_WIN void OnUsbContext(scoped_refptr<UsbContext> context); @@ -101,7 +101,7 @@ class UsbServiceImpl final : // Enumeration callbacks are queued until an enumeration completes. bool enumeration_ready_ = false; bool enumeration_in_progress_ = false; - base::queue<base::string16> pending_path_enumerations_; + base::queue<std::wstring> pending_path_enumerations_; std::vector<GetDevicesCallback> pending_enumeration_callbacks_; // The map from libusb_device to UsbDeviceImpl. The key is a weak pointer to diff --git a/chromium/services/device/usb/usb_service_linux.cc b/chromium/services/device/usb/usb_service_linux.cc index 7e7eb833b76..2328c9300bb 100644 --- a/chromium/services/device/usb/usb_service_linux.cc +++ b/chromium/services/device/usb/usb_service_linux.cc @@ -149,8 +149,9 @@ void UsbServiceLinux::BlockingTaskRunnerHelper::OnDeviceAdded( return; std::unique_ptr<UsbDeviceDescriptor> descriptor(new UsbDeviceDescriptor()); - if (!descriptor->Parse(std::vector<uint8_t>(descriptors_str.begin(), - descriptors_str.end()))) { + if (!descriptor->Parse(base::make_span( + reinterpret_cast<const uint8_t*>(descriptors_str.data()), + descriptors_str.size()))) { return; } diff --git a/chromium/services/device/usb/usb_service_mac.cc b/chromium/services/device/usb/usb_service_mac.cc new file mode 100644 index 00000000000..ecf36d7e6ea --- /dev/null +++ b/chromium/services/device/usb/usb_service_mac.cc @@ -0,0 +1,326 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/device/usb/usb_service_mac.h" + +#include <CoreFoundation/CFBase.h> +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOReturn.h> + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_ioplugininterface.h" +#include "base/optional.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "components/device_event_log/device_event_log.h" +#include "services/device/usb/usb_descriptors.h" +#include "services/device/usb/usb_device_mac.h" + +namespace device { + +namespace { + +// USB class codes are defined by the USB specification. +// https://www.usb.org/defined-class-codes +constexpr uint8_t kDeviceClassHub = 0x09; + +// These methods are similar to the ones used by HidServiceMac. +// TODO(https://crbug.com/1104271): Move these methods into a shared utility +// file. +base::Optional<base::string16> GetStringProperty(io_service_t service, + CFStringRef key) { + base::ScopedCFTypeRef<CFStringRef> ref(base::mac::CFCast<CFStringRef>( + IORegistryEntryCreateCFProperty(service, key, kCFAllocatorDefault, 0))); + + if (!ref) + return base::nullopt; + + return base::SysCFStringRefToUTF16(ref); +} + +base::Optional<uint16_t> GetUint16Property(io_service_t service, + CFStringRef property) { + base::ScopedCFTypeRef<CFNumberRef> cf_number( + base::mac::CFCast<CFNumberRef>(IORegistryEntryCreateCFProperty( + service, property, kCFAllocatorDefault, 0))); + + if (!cf_number) + return base::nullopt; + if (CFGetTypeID(cf_number) != CFNumberGetTypeID()) + return base::nullopt; + uint16_t value; + if (!CFNumberGetValue((CFNumberRef)cf_number, kCFNumberSInt16Type, &value)) + return base::nullopt; + return value; +} + +base::Optional<uint8_t> GetUint8Property(io_service_t service, + CFStringRef property) { + base::ScopedCFTypeRef<CFNumberRef> cf_number( + base::mac::CFCast<CFNumberRef>(IORegistryEntryCreateCFProperty( + service, property, kCFAllocatorDefault, 0))); + + bool success = false; + uint8_t value; + if (cf_number) { + if (CFGetTypeID(cf_number) == CFNumberGetTypeID()) { + success = + CFNumberGetValue((CFNumberRef)cf_number, kCFNumberSInt8Type, &value); + } + } + + if (success) + return value; + + return base::nullopt; +} + +} // namespace + +UsbServiceMac::UsbServiceMac() { + notify_port_.reset(IONotificationPortCreate(kIOMasterPortDefault)); + CFRunLoopAddSource(CFRunLoopGetMain(), + IONotificationPortGetRunLoopSource(notify_port_.get()), + kCFRunLoopDefaultMode); + + IOReturn result = IOServiceAddMatchingNotification( + notify_port_.get(), kIOFirstMatchNotification, + IOServiceMatching(kIOUSBDeviceClassName), FirstMatchCallback, this, + devices_added_iterator_.InitializeInto()); + if (result != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to listen for device arrival: " << std::hex + << result << "."; + return; + } + // Drain |devices_added_iterator_| to arm the notification. + AddDevices(); + + result = IOServiceAddMatchingNotification( + notify_port_.get(), kIOTerminatedNotification, + IOServiceMatching(kIOUSBDeviceClassName), TerminatedCallback, this, + devices_removed_iterator_.InitializeInto()); + if (result != kIOReturnSuccess) { + USB_LOG(ERROR) << "Failed to listen for device removal: " << std::hex + << result << "."; + return; + } + + // Drain |devices_removed_iterator_| to arm the notification. + RemoveDevices(); +} + +UsbServiceMac::~UsbServiceMac() = default; + +// static +void UsbServiceMac::FirstMatchCallback(void* context, io_iterator_t iterator) { + DCHECK_EQ(CFRunLoopGetMain(), CFRunLoopGetCurrent()); + UsbServiceMac* service = reinterpret_cast<UsbServiceMac*>(context); + DCHECK_EQ(service->devices_added_iterator_, iterator); + service->AddDevices(); +} + +// static +void UsbServiceMac::TerminatedCallback(void* context, io_iterator_t iterator) { + DCHECK_EQ(CFRunLoopGetMain(), CFRunLoopGetCurrent()); + UsbServiceMac* service = reinterpret_cast<UsbServiceMac*>(context); + DCHECK_EQ(service->devices_removed_iterator_, iterator); + service->RemoveDevices(); +} + +void UsbServiceMac::AddDevices() { + base::mac::ScopedIOObject<io_service_t> device; + while (device.reset(IOIteratorNext(devices_added_iterator_)), device) { + AddDevice(device); + } +} + +void UsbServiceMac::AddDevice(io_service_t device) { + base::mac::ScopedIOPluginInterface<IOCFPlugInInterface> plugin_interface; + int32_t score; + + // This call fails sometimes due to a resource shortage. + // TODO(richardmachado): Figure out what is causing this failure. + IOReturn kr = IOCreatePlugInInterfaceForService( + device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, + plugin_interface.InitializeInto(), &score); + + if ((kr != kIOReturnSuccess) || !plugin_interface.get()) { + USB_LOG(ERROR) << "Unable to create a plug-in: " << std::hex << kr << "."; + return; + } + + base::mac::ScopedIOPluginInterface<IOUSBDeviceInterface182> device_interface; + kr = (*plugin_interface) + ->QueryInterface( + plugin_interface.get(), + CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), + reinterpret_cast<LPVOID*>(device_interface.InitializeInto())); + + if (kr != kIOReturnSuccess || !device_interface) { + USB_LOG(ERROR) << "Couldn’t create a device interface."; + return; + } + + uint8_t device_class; + if ((*device_interface)->GetDeviceClass(device_interface, &device_class) != + kIOReturnSuccess) { + return; + } + + // We don't want to enumerate hubs. + if (device_class == kDeviceClassHub) + return; + + uint16_t vendor_id; + if ((*device_interface)->GetDeviceVendor(device_interface, &vendor_id) != + kIOReturnSuccess) { + return; + } + + uint16_t product_id; + if ((*device_interface)->GetDeviceProduct(device_interface, &product_id) != + kIOReturnSuccess) { + return; + } + + uint8_t device_protocol; + if ((*device_interface) + ->GetDeviceProtocol(device_interface, &device_protocol) != + kIOReturnSuccess) { + return; + } + + uint8_t device_subclass; + if ((*device_interface) + ->GetDeviceSubClass(device_interface, &device_subclass) != + kIOReturnSuccess) { + return; + } + + uint16_t device_version; + if ((*device_interface) + ->GetDeviceReleaseNumber(device_interface, &device_version) != + kIOReturnSuccess) { + return; + } + + uint32_t location_id; + if ((*device_interface)->GetLocationID(device_interface, &location_id) != + kIOReturnSuccess) { + return; + } + + uint64_t entry_id; + if (IORegistryEntryGetRegistryEntryID(device, &entry_id) != kIOReturnSuccess) + return; + + base::Optional<uint8_t> property_uint8 = + GetUint8Property(device, CFSTR("PortNum")); + if (!property_uint8.has_value()) + return; + uint8_t port_number = property_uint8.value(); + + base::Optional<uint16_t> property_uint16 = + GetUint16Property(device, CFSTR("bcdUSB")); + uint16_t usb_version; + if (!property_uint16.has_value()) + return; + usb_version = property_uint16.value(); + + base::Optional<base::string16> property_string16 = + GetStringProperty(device, CFSTR(kUSBVendorString)); + base::string16 manufacturer_string; + if (property_string16.has_value()) + manufacturer_string = property_string16.value(); + + property_string16 = GetStringProperty(device, CFSTR(kUSBSerialNumberString)); + base::string16 serial_number_string; + if (property_string16.has_value()) + serial_number_string = property_string16.value(); + + property_string16 = GetStringProperty(device, CFSTR(kUSBProductString)); + base::string16 product_string; + if (property_string16.has_value()) + product_string = property_string16.value(); + + uint8_t num_config; + if ((*device_interface) + ->GetNumberOfConfigurations(device_interface, &num_config) != + kIOReturnSuccess) { + return; + } + + // Populate device descriptor with all necessary configuration info. + auto descriptor = std::make_unique<UsbDeviceDescriptor>(); + IOUSBConfigurationDescriptorPtr desc; + for (uint8_t i = 0; i < num_config; i++) { + if ((*device_interface) + ->GetConfigurationDescriptorPtr(device_interface, i, &desc) != + kIOReturnSuccess) { + return; + } + if (!descriptor->Parse(base::make_span(reinterpret_cast<uint8_t*>(desc), + desc->wTotalLength))) { + return; + } + } + + descriptor->device_info->usb_version_major = usb_version >> 8; + descriptor->device_info->usb_version_minor = usb_version >> 4 & 0xf; + descriptor->device_info->usb_version_subminor = usb_version & 0xf; + descriptor->device_info->class_code = device_class; + descriptor->device_info->subclass_code = device_subclass; + descriptor->device_info->protocol_code = device_protocol; + descriptor->device_info->vendor_id = vendor_id; + descriptor->device_info->product_id = product_id; + descriptor->device_info->device_version_major = device_version >> 8; + descriptor->device_info->device_version_minor = device_version >> 4 & 0xf; + descriptor->device_info->device_version_subminor = device_version & 0xf; + descriptor->device_info->manufacturer_name = manufacturer_string; + descriptor->device_info->product_name = product_string; + descriptor->device_info->serial_number = serial_number_string; + descriptor->device_info->bus_number = location_id >> 24; + descriptor->device_info->port_number = port_number; + + scoped_refptr<UsbDeviceMac> mac_device = + new UsbDeviceMac(entry_id, std::move(descriptor->device_info)); + + device_map_[entry_id] = mac_device; + devices()[mac_device->guid()] = mac_device; + + NotifyDeviceAdded(mac_device); +} + +void UsbServiceMac::RemoveDevices() { + base::mac::ScopedIOObject<io_service_t> device; + while (device.reset(IOIteratorNext(devices_removed_iterator_)), device) { + uint64_t entry_id; + + if (kIOReturnSuccess != + IORegistryEntryGetRegistryEntryID(device, &entry_id)) { + continue; + } + + auto it = device_map_.find(entry_id); + if (it == device_map_.end()) + continue; + + auto mac_device = it->second; + device_map_.erase(it); + + auto by_guid_it = devices().find(mac_device->guid()); + devices().erase(by_guid_it); + NotifyDeviceRemoved(mac_device); + mac_device->OnDisconnect(); + } +} + +} // namespace device diff --git a/chromium/services/device/usb/usb_service_mac.h b/chromium/services/device/usb/usb_service_mac.h new file mode 100644 index 00000000000..41457029e51 --- /dev/null +++ b/chromium/services/device/usb/usb_service_mac.h @@ -0,0 +1,55 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_DEVICE_USB_USB_SERVICE_MAC_H_ +#define SERVICES_DEVICE_USB_USB_SERVICE_MAC_H_ + +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/usb/IOUSBLib.h> + +#include <string> +#include <unordered_map> + +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_ionotificationportref.h" +#include "base/mac/scoped_ioobject.h" +#include "base/memory/weak_ptr.h" +#include "services/device/usb/usb_service.h" + +namespace device { + +class UsbDeviceMac; + +// The USB service is responsible for device discovery on the system, which +// allows it to re-use device handles to prevent competition for the same USB +// device. +class UsbServiceMac final : public UsbService { + public: + UsbServiceMac(); + UsbServiceMac(const UsbServiceMac&) = delete; + UsbServiceMac& operator=(const UsbServiceMac&) = delete; + ~UsbServiceMac() override; + + private: + // IOService matching callbacks. + static void FirstMatchCallback(void* context, io_iterator_t iterator); + static void TerminatedCallback(void* context, io_iterator_t iterator); + + void AddDevices(); + void AddDevice(io_service_t device); + void RemoveDevices(); + + std::unordered_map<uint64_t, scoped_refptr<UsbDeviceMac>> device_map_; + + base::mac::ScopedIONotificationPortRef notify_port_; + base::mac::ScopedIOObject<io_iterator_t> devices_added_iterator_; + base::mac::ScopedIOObject<io_iterator_t> devices_removed_iterator_; + + base::WeakPtrFactory<UsbServiceMac> weak_factory_{this}; +}; + +} // namespace device + +#endif // SERVICES_DEVICE_USB_USB_SERVICE_MAC_H_ diff --git a/chromium/services/device/usb/usb_service_win.cc b/chromium/services/device/usb/usb_service_win.cc index 4e8989f6d3d..587fe95742a 100644 --- a/chromium/services/device/usb/usb_service_win.cc +++ b/chromium/services/device/usb/usb_service_win.cc @@ -8,6 +8,7 @@ #include <setupapi.h> #include <stdint.h> #include <usbiodef.h> +#include "base/strings/string_piece_forward.h" #define INITGUID #include <devpkey.h> @@ -59,7 +60,7 @@ base::Optional<uint32_t> GetDeviceUint32Property(HDEVINFO dev_info, return buffer; } -base::Optional<base::string16> GetDeviceStringProperty( +base::Optional<std::wstring> GetDeviceStringProperty( HDEVINFO dev_info, SP_DEVINFO_DATA* dev_info_data, const DEVPROPKEY& property) { @@ -76,7 +77,7 @@ base::Optional<base::string16> GetDeviceStringProperty( return base::nullopt; } - base::string16 buffer; + std::wstring buffer; if (!SetupDiGetDeviceProperty( dev_info, dev_info_data, &property, &property_type, reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)), @@ -87,7 +88,7 @@ base::Optional<base::string16> GetDeviceStringProperty( return buffer; } -base::Optional<std::vector<base::string16>> GetDeviceStringListProperty( +base::Optional<std::vector<std::wstring>> GetDeviceStringListProperty( HDEVINFO dev_info, SP_DEVINFO_DATA* dev_info_data, const DEVPROPKEY& property) { @@ -104,7 +105,7 @@ base::Optional<std::vector<base::string16>> GetDeviceStringListProperty( return base::nullopt; } - base::string16 buffer; + std::wstring buffer; if (!SetupDiGetDeviceProperty( dev_info, dev_info_data, &property, &property_type, reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)), @@ -113,33 +114,32 @@ base::Optional<std::vector<base::string16>> GetDeviceStringListProperty( } // Windows string list properties use a NUL character as the delimiter. - return base::SplitString(buffer, base::StringPiece16(L"\0", 1), + return base::SplitString(buffer, base::WStringPiece(L"\0", 1), base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); } -base::string16 GetServiceName(HDEVINFO dev_info, - SP_DEVINFO_DATA* dev_info_data) { - base::Optional<base::string16> property = +std::wstring GetServiceName(HDEVINFO dev_info, SP_DEVINFO_DATA* dev_info_data) { + base::Optional<std::wstring> property = GetDeviceStringProperty(dev_info, dev_info_data, DEVPKEY_Device_Service); if (!property.has_value()) - return base::string16(); + return std::wstring(); // Windows pads this string with a variable number of NUL bytes for no // discernible reason. - return base::TrimString(*property, base::StringPiece16(L"\0", 1), + return base::TrimString(*property, base::WStringPiece(L"\0", 1), base::TRIM_TRAILING) .as_string(); } bool GetDeviceInterfaceDetails(HDEVINFO dev_info, SP_DEVICE_INTERFACE_DATA* device_interface_data, - base::string16* device_path, + std::wstring* device_path, uint32_t* bus_number, uint32_t* port_number, - base::string16* instance_id, - base::string16* parent_instance_id, - std::vector<base::string16>* child_instance_ids, - base::string16* service_name) { + std::wstring* instance_id, + std::wstring* parent_instance_id, + std::vector<std::wstring>* child_instance_ids, + std::wstring* service_name) { SP_DEVINFO_DATA dev_info_data = {}; dev_info_data.cbSize = sizeof(dev_info_data); @@ -175,7 +175,7 @@ bool GetDeviceInterfaceDetails(HDEVINFO dev_info, } if (device_path) - *device_path = base::string16(device_interface_detail_data->DevicePath); + *device_path = std::wstring(device_interface_detail_data->DevicePath); if (bus_number) { auto result = GetDeviceUint32Property(dev_info, &dev_info_data, @@ -242,14 +242,14 @@ bool GetDeviceInterfaceDetails(HDEVINFO dev_info, return true; } -base::string16 GetDevicePath(const base::string16& instance_id, - const GUID& device_interface_guid) { +std::wstring GetDevicePath(const std::wstring& instance_id, + const GUID& device_interface_guid) { base::win::ScopedDevInfo dev_info( SetupDiGetClassDevs(&device_interface_guid, instance_id.c_str(), 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT)); if (!dev_info.is_valid()) { USB_PLOG(ERROR) << "SetupDiGetClassDevs"; - return base::string16(); + return std::wstring(); } SP_DEVICE_INTERFACE_DATA device_interface_data = {}; @@ -258,28 +258,28 @@ base::string16 GetDevicePath(const base::string16& instance_id, &device_interface_guid, 0, &device_interface_data)) { USB_PLOG(ERROR) << "SetupDiEnumDeviceInterfaces"; - return base::string16(); + return std::wstring(); } - base::string16 device_path; + std::wstring device_path; if (!GetDeviceInterfaceDetails( dev_info.get(), &device_interface_data, &device_path, /*bus_number=*/nullptr, /*port_number=*/nullptr, /*instance_id=*/nullptr, /*parent_instance_id=*/nullptr, /*child_instance_ids=*/nullptr, /*service_name=*/nullptr)) { - return base::string16(); + return std::wstring(); } return device_path; } -int GetInterfaceNumber(const base::string16& instance_id) { +int GetInterfaceNumber(const std::wstring& instance_id) { // According to MSDN the instance IDs for the device nodes created by the // composite driver is in the form "USB\VID_vvvv&PID_dddd&MI_zz" where "zz" // is the interface number. // // https://docs.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers#multiple-interface-usb-devices - std::string instance_id_ascii = base::UTF16ToASCII(instance_id); + std::string instance_id_ascii = base::WideToASCII(instance_id); std::string interface_number_str; if (!RE2::PartialMatch(instance_id_ascii, "MI_([0-9a-fA-F]{2})", &interface_number_str)) { @@ -292,7 +292,7 @@ int GetInterfaceNumber(const base::string16& instance_id) { return interface_number; } -UsbDeviceWin::FunctionInfo GetFunctionInfo(const base::string16& instance_id) { +UsbDeviceWin::FunctionInfo GetFunctionInfo(const std::wstring& instance_id) { UsbDeviceWin::FunctionInfo info; base::win::ScopedDevInfo dev_info( @@ -404,7 +404,7 @@ class UsbServiceWin::BlockingTaskRunnerHelper { FROM_HERE, base::BindOnce(&UsbServiceWin::HelperStarted, service_)); } - void OnDeviceAdded(const GUID& guid, const base::string16& device_path) { + void OnDeviceAdded(const GUID& guid, const std::wstring& device_path) { // Boost priority while potentially loading SetupAPI.dll and Ole32.dll on a // background thread for the following functions. SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY(); @@ -435,9 +435,9 @@ class UsbServiceWin::BlockingTaskRunnerHelper { private: void EnumerateDevice(HDEVINFO dev_info, SP_DEVICE_INTERFACE_DATA* device_interface_data, - const base::Optional<base::string16>& opt_device_path) { - base::string16 device_path; - base::string16* device_path_ptr = &device_path; + const base::Optional<std::wstring>& opt_device_path) { + std::wstring device_path; + std::wstring* device_path_ptr = &device_path; if (opt_device_path) { device_path = *opt_device_path; device_path_ptr = nullptr; @@ -445,9 +445,9 @@ class UsbServiceWin::BlockingTaskRunnerHelper { uint32_t bus_number; uint32_t port_number; - base::string16 parent_instance_id; - std::vector<base::string16> child_instance_ids; - base::string16 service_name; + std::wstring parent_instance_id; + std::vector<std::wstring> child_instance_ids; + std::wstring service_name; if (!GetDeviceInterfaceDetails(dev_info, device_interface_data, device_path_ptr, &bus_number, &port_number, /*instance_id=*/nullptr, &parent_instance_id, @@ -461,7 +461,7 @@ class UsbServiceWin::BlockingTaskRunnerHelper { // child device nodes for each of the device functions. It is these device // paths for these children which must be opened in order to communicate // with the WinUSB driver. - for (const base::string16& instance_id : child_instance_ids) { + for (const std::wstring& instance_id : child_instance_ids) { int interface_number = GetInterfaceNumber(instance_id); if (interface_number != -1) { functions.emplace_back(interface_number, @@ -477,7 +477,7 @@ class UsbServiceWin::BlockingTaskRunnerHelper { functions.emplace_back(/*interface_number=*/0, info); } - base::string16& hub_path = hub_paths_[parent_instance_id]; + std::wstring& hub_path = hub_paths_[parent_instance_id]; if (hub_path.empty()) { hub_path = GetDevicePath(parent_instance_id, GUID_DEVINTERFACE_USB_HUB); if (hub_path.empty()) @@ -494,10 +494,10 @@ class UsbServiceWin::BlockingTaskRunnerHelper { void EnumeratePotentialFunction( HDEVINFO dev_info, SP_DEVICE_INTERFACE_DATA* device_interface_data, - const base::string16& device_path) { - base::string16 instance_id; - base::string16 parent_instance_id; - base::string16 service_name; + const std::wstring& device_path) { + std::wstring instance_id; + std::wstring parent_instance_id; + std::wstring service_name; if (!GetDeviceInterfaceDetails( dev_info, device_interface_data, /*device_path=*/nullptr, /*bus_number=*/nullptr, @@ -510,7 +510,7 @@ class UsbServiceWin::BlockingTaskRunnerHelper { if (interface_number == -1) return; - base::string16 parent_path = + std::wstring parent_path = GetDevicePath(parent_instance_id, GUID_DEVINTERFACE_USB_DEVICE); if (parent_path.empty()) return; @@ -525,7 +525,7 @@ class UsbServiceWin::BlockingTaskRunnerHelper { std::move(parent_path), interface_number, info)); } - std::unordered_map<base::string16, base::string16> hub_paths_; + std::unordered_map<std::wstring, std::wstring> hub_paths_; // Calls back to |service_| must be posted to |service_task_runner_|, which // runs tasks on the thread where that object lives. @@ -561,7 +561,7 @@ void UsbServiceWin::GetDevices(GetDevicesCallback callback) { } void UsbServiceWin::OnDeviceAdded(const GUID& class_guid, - const base::string16& device_path) { + const std::wstring& device_path) { blocking_task_runner_->PostTask( FROM_HERE, base::BindOnce(&BlockingTaskRunnerHelper::OnDeviceAdded, @@ -569,7 +569,7 @@ void UsbServiceWin::OnDeviceAdded(const GUID& class_guid, } void UsbServiceWin::OnDeviceRemoved(const GUID& class_guid, - const base::string16& device_path) { + const std::wstring& device_path) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); auto by_path_it = devices_by_path_.find(device_path); if (by_path_it == devices_by_path_.end()) @@ -604,12 +604,12 @@ void UsbServiceWin::HelperStarted() { } void UsbServiceWin::CreateDeviceObject( - const base::string16& device_path, - const base::string16& hub_path, + const std::wstring& device_path, + const std::wstring& hub_path, const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions, uint32_t bus_number, uint32_t port_number, - const base::string16& driver_name) { + const std::wstring& driver_name) { // Devices that appear during initial enumeration are gathered into the first // result returned by GetDevices() and prevent device add/remove notifications // from being sent. @@ -624,7 +624,7 @@ void UsbServiceWin::CreateDeviceObject( } void UsbServiceWin::UpdateFunction( - const base::string16& device_path, + const std::wstring& device_path, int interface_number, const UsbDeviceWin::FunctionInfo& function_info) { auto it = devices_by_path_.find(device_path); diff --git a/chromium/services/device/usb/usb_service_win.h b/chromium/services/device/usb/usb_service_win.h index 78dfbf559f6..82acab8c681 100644 --- a/chromium/services/device/usb/usb_service_win.h +++ b/chromium/services/device/usb/usb_service_win.h @@ -35,20 +35,20 @@ class UsbServiceWin final : public DeviceMonitorWin::Observer, // device::DeviceMonitorWin::Observer implementation void OnDeviceAdded(const GUID& class_guid, - const base::string16& device_path) override; + const std::wstring& device_path) override; void OnDeviceRemoved(const GUID& class_guid, - const base::string16& device_path) override; + const std::wstring& device_path) override; // Methods called by BlockingThreadHelper void HelperStarted(); void CreateDeviceObject( - const base::string16& device_path, - const base::string16& hub_path, + const std::wstring& device_path, + const std::wstring& hub_path, const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions, uint32_t bus_number, uint32_t port_number, - const base::string16& driver_name); - void UpdateFunction(const base::string16& device_path, + const std::wstring& driver_name); + void UpdateFunction(const std::wstring& device_path, int interface_number, const UsbDeviceWin::FunctionInfo& function_info); @@ -65,7 +65,7 @@ class UsbServiceWin final : public DeviceMonitorWin::Observer, scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; std::unique_ptr<BlockingTaskRunnerHelper, base::OnTaskRunnerDeleter> helper_; - std::unordered_map<base::string16, scoped_refptr<UsbDeviceWin>> + std::unordered_map<std::wstring, scoped_refptr<UsbDeviceWin>> devices_by_path_; ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_; diff --git a/chromium/services/device/wake_lock/power_save_blocker/BUILD.gn b/chromium/services/device/wake_lock/power_save_blocker/BUILD.gn index 9365bd609d5..c3b7727b181 100644 --- a/chromium/services/device/wake_lock/power_save_blocker/BUILD.gn +++ b/chromium/services/device/wake_lock/power_save_blocker/BUILD.gn @@ -40,18 +40,21 @@ source_set("power_save_blocker") { "//chromeos/dbus/power", "//chromeos/dbus/power:power_manager_proto", ] - } else if (is_linux && use_dbus) { + } else if ((is_linux || is_chromeos) && use_dbus) { sources += [ "power_save_blocker_linux.cc" ] deps += [ "//dbus", "//ui/gfx", ] if (use_x11) { - deps += [ "//ui/gfx/x" ] + deps += [ + "//ui/base:features", + "//ui/gfx/x", + ] } } else if (is_mac) { sources += [ "power_save_blocker_mac.cc" ] - libs = [ + frameworks = [ "CoreFoundation.framework", "IOKit.framework", ] diff --git a/chromium/services/device/wake_lock/power_save_blocker/DEPS b/chromium/services/device/wake_lock/power_save_blocker/DEPS index 8ab9716e496..3b6ce5f9f9d 100644 --- a/chromium/services/device/wake_lock/power_save_blocker/DEPS +++ b/chromium/services/device/wake_lock/power_save_blocker/DEPS @@ -4,4 +4,5 @@ include_rules = [ "+services/device/public", "+ui/android", "+ui/gfx", + "+ui/base/ui_base_features.h", ] diff --git a/chromium/services/device/wake_lock/power_save_blocker/power_save_blocker_linux.cc b/chromium/services/device/wake_lock/power_save_blocker/power_save_blocker_linux.cc index 6b28c96b7dc..3db98411212 100644 --- a/chromium/services/device/wake_lock/power_save_blocker/power_save_blocker_linux.cc +++ b/chromium/services/device/wake_lock/power_save_blocker/power_save_blocker_linux.cc @@ -26,9 +26,10 @@ #include "ui/gfx/switches.h" #if defined(USE_X11) -#include "ui/gfx/x/connection.h" // nogncheck -#include "ui/gfx/x/screensaver.h" // nogncheck -#include "ui/gfx/x/x11_types.h" // nogncheck +#include "ui/base/ui_base_features.h" // nogncheck +#include "ui/gfx/x/connection.h" // nogncheck +#include "ui/gfx/x/screensaver.h" // nogncheck +#include "ui/gfx/x/x11_types.h" // nogncheck #endif namespace device { @@ -242,8 +243,10 @@ void PowerSaveBlocker::Delegate::Init() { } #if defined(USE_X11) - ui_task_runner_->PostTask(FROM_HERE, - base::BindOnce(X11ScreenSaverSuspendSet, true)); + if (!features::IsUsingOzonePlatform()) { + ui_task_runner_->PostTask(FROM_HERE, + base::BindOnce(X11ScreenSaverSuspendSet, true)); + } #endif } @@ -254,8 +257,10 @@ void PowerSaveBlocker::Delegate::CleanUp() { } #if defined(USE_X11) - ui_task_runner_->PostTask(FROM_HERE, - base::BindOnce(X11ScreenSaverSuspendSet, false)); + if (!features::IsUsingOzonePlatform()) { + ui_task_runner_->PostTask(FROM_HERE, + base::BindOnce(X11ScreenSaverSuspendSet, false)); + } #endif } diff --git a/chromium/services/device/wake_lock/power_save_blocker/power_save_blocker_win.cc b/chromium/services/device/wake_lock/power_save_blocker/power_save_blocker_win.cc index bfb794c54ee..b4b31fb2c8a 100644 --- a/chromium/services/device/wake_lock/power_save_blocker/power_save_blocker_win.cc +++ b/chromium/services/device/wake_lock/power_save_blocker/power_save_blocker_win.cc @@ -24,7 +24,7 @@ HANDLE CreatePowerRequest(POWER_REQUEST_TYPE type, return INVALID_HANDLE_VALUE; } - base::string16 wide_description = base::ASCIIToUTF16(description); + std::wstring wide_description = base::ASCIIToWide(description); REASON_CONTEXT context = {0}; context.Version = POWER_REQUEST_CONTEXT_VERSION; context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; diff --git a/chromium/services/device/wake_lock/wake_lock_provider.h b/chromium/services/device/wake_lock/wake_lock_provider.h index f9e252fa488..1b951ab031c 100644 --- a/chromium/services/device/wake_lock/wake_lock_provider.h +++ b/chromium/services/device/wake_lock/wake_lock_provider.h @@ -11,7 +11,6 @@ #include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" -#include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver_set.h" diff --git a/chromium/services/device/wake_lock/wake_lock_unittest.cc b/chromium/services/device/wake_lock/wake_lock_unittest.cc index e7eec1bab17..f0dca917942 100644 --- a/chromium/services/device/wake_lock/wake_lock_unittest.cc +++ b/chromium/services/device/wake_lock/wake_lock_unittest.cc @@ -9,7 +9,6 @@ #include "base/bind.h" #include "base/run_loop.h" -#include "mojo/public/cpp/bindings/interface_ptr.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver_set.h" diff --git a/chromium/services/image_annotation/public/cpp/image_processor.cc b/chromium/services/image_annotation/public/cpp/image_processor.cc index 562d73e2ef4..2bedb4203e0 100644 --- a/chromium/services/image_annotation/public/cpp/image_processor.cc +++ b/chromium/services/image_annotation/public/cpp/image_processor.cc @@ -8,7 +8,6 @@ #include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/task_runner_util.h" -#include "mojo/public/cpp/bindings/interface_request.h" #include "services/image_annotation/image_annotation_metrics.h" #include "third_party/skia/include/core/SkCanvas.h" #include "ui/gfx/codec/jpeg_codec.h" diff --git a/chromium/services/media_session/audio_focus_request.cc b/chromium/services/media_session/audio_focus_request.cc index a304a722e4b..b3346758bc6 100644 --- a/chromium/services/media_session/audio_focus_request.cc +++ b/chromium/services/media_session/audio_focus_request.cc @@ -125,13 +125,14 @@ void AudioFocusRequest::ReleaseTransientHold() { return; was_suspended_ = false; - session_->Resume(mojom::MediaSession::SuspendType::kSystem); - if (!delayed_action_) + if (delayed_action_) { + PerformUIAction(*delayed_action_); + delayed_action_.reset(); return; + } - PerformUIAction(*delayed_action_); - delayed_action_.reset(); + session_->Resume(mojom::MediaSession::SuspendType::kSystem); } void AudioFocusRequest::PerformUIAction(mojom::MediaSessionAction action) { diff --git a/chromium/services/media_session/media_controller.cc b/chromium/services/media_session/media_controller.cc index 8ff49e78707..483b20b9980 100644 --- a/chromium/services/media_session/media_controller.cc +++ b/chromium/services/media_session/media_controller.cc @@ -291,6 +291,13 @@ void MediaController::ExitPictureInPicture() { session_->ipc()->ExitPictureInPicture(); } +void MediaController::SetAudioSinkId(const base::Optional<std::string>& id) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (session_) + session_->ipc()->SetAudioSinkId(id); +} + void MediaController::SetMediaSession(AudioFocusRequest* session) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); diff --git a/chromium/services/media_session/media_controller.h b/chromium/services/media_session/media_controller.h index 6c9518eb28a..349199e2ae1 100644 --- a/chromium/services/media_session/media_controller.h +++ b/chromium/services/media_session/media_controller.h @@ -53,6 +53,7 @@ class MediaController : public mojom::MediaController, void ScrubTo(base::TimeDelta seek_time) override; void EnterPictureInPicture() override; void ExitPictureInPicture() override; + void SetAudioSinkId(const base::Optional<std::string>& id) override; // mojom::MediaSessionObserver overrides. void MediaSessionInfoChanged( diff --git a/chromium/services/media_session/public/cpp/OWNERS b/chromium/services/media_session/public/cpp/OWNERS index 7aebc8abbf8..d5fefd82012 100644 --- a/chromium/services/media_session/public/cpp/OWNERS +++ b/chromium/services/media_session/public/cpp/OWNERS @@ -1,4 +1,2 @@ per-file *_mojom_traits*.*=set noparent per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS diff --git a/chromium/services/media_session/public/cpp/android/BUILD.gn b/chromium/services/media_session/public/cpp/android/BUILD.gn index 0f863dce22f..ffa37cf7b70 100644 --- a/chromium/services/media_session/public/cpp/android/BUILD.gn +++ b/chromium/services/media_session/public/cpp/android/BUILD.gn @@ -16,7 +16,10 @@ generate_jni("media_session_jni_headers") { if (current_toolchain == default_toolchain) { android_library("media_session_java") { - deps = [ "//base:base_java" ] + deps = [ + "//base:base_java", + "//third_party/android_deps:androidx_annotation_annotation_java", + ] sources = _jni_sources } } diff --git a/chromium/services/media_session/public/cpp/media_session.typemap b/chromium/services/media_session/public/cpp/media_session.typemap deleted file mode 100644 index 2eebff2348f..00000000000 --- a/chromium/services/media_session/public/cpp/media_session.typemap +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2018 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//services/media_session/public/mojom/media_session.mojom" -public_headers = [ - "//services/media_session/public/cpp/media_image.h", - "//services/media_session/public/cpp/media_metadata.h", - "//services/media_session/public/cpp/media_position.h", - "//third_party/skia/include/core/SkBitmap.h", -] -traits_headers = - [ "//services/media_session/public/cpp/media_session_mojom_traits.h" ] -public_deps = [ - "//services/media_session/public/cpp:base_cpp", - "//skia", -] -deps = [ - "//ui/gfx/geometry/mojom:mojom_traits", -] -type_mappings = [ - "media_session.mojom.MediaImage=::media_session::MediaImage", - "media_session.mojom.MediaImageBitmap=::SkBitmap[nullable_is_same_type]", - "media_session.mojom.MediaMetadata=::media_session::MediaMetadata", - "media_session.mojom.MediaPosition=::media_session::MediaPosition", -] -sources = [ - "//services/media_session/public/cpp/media_session_mojom_traits.cc", - "//services/media_session/public/cpp/media_session_mojom_traits.h", -] diff --git a/chromium/services/media_session/public/cpp/media_session_mojom_traits.cc b/chromium/services/media_session/public/cpp/media_session_mojom_traits.cc index f9beae31815..c7f5830504a 100644 --- a/chromium/services/media_session/public/cpp/media_session_mojom_traits.cc +++ b/chromium/services/media_session/public/cpp/media_session_mojom_traits.cc @@ -6,7 +6,6 @@ #include "mojo/public/cpp/base/string16_mojom_traits.h" #include "mojo/public/cpp/base/time_mojom_traits.h" -#include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/mojom/geometry_mojom_traits.h" #include "url/mojom/url_gurl_mojom_traits.h" diff --git a/chromium/services/media_session/public/cpp/media_session_mojom_traits.h b/chromium/services/media_session/public/cpp/media_session_mojom_traits.h index 9051f6a4ef5..a049254da11 100644 --- a/chromium/services/media_session/public/cpp/media_session_mojom_traits.h +++ b/chromium/services/media_session/public/cpp/media_session_mojom_traits.h @@ -8,7 +8,13 @@ #include <vector> #include "base/containers/span.h" -#include "services/media_session/public/mojom/media_session.mojom.h" +#include "base/strings/string16.h" +#include "services/media_session/public/cpp/media_image.h" +#include "services/media_session/public/cpp/media_metadata.h" +#include "services/media_session/public/cpp/media_position.h" +#include "services/media_session/public/mojom/media_session.mojom-shared.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/geometry/size.h" namespace mojo { diff --git a/chromium/services/media_session/public/cpp/mojom_traits_unittest.cc b/chromium/services/media_session/public/cpp/mojom_traits_unittest.cc index 7e8c609c210..0ad144e499c 100644 --- a/chromium/services/media_session/public/cpp/mojom_traits_unittest.cc +++ b/chromium/services/media_session/public/cpp/mojom_traits_unittest.cc @@ -4,6 +4,7 @@ #include "mojo/public/cpp/test_support/test_utils.h" #include "services/media_session/public/cpp/media_session_mojom_traits.h" +#include "services/media_session/public/mojom/media_session.mojom.h" #include "testing/gtest/include/gtest/gtest.h" using media_session::mojom::MediaImageBitmap; diff --git a/chromium/services/media_session/public/cpp/typemaps.gni b/chromium/services/media_session/public/cpp/typemaps.gni deleted file mode 100644 index 280e7e00003..00000000000 --- a/chromium/services/media_session/public/cpp/typemaps.gni +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright 2018 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -typemaps = [ "//services/media_session/public/cpp/media_session.typemap" ] diff --git a/chromium/services/media_session/public/mojom/BUILD.gn b/chromium/services/media_session/public/mojom/BUILD.gn index 1f30a298d32..4ee7adcc43e 100644 --- a/chromium/services/media_session/public/mojom/BUILD.gn +++ b/chromium/services/media_session/public/mojom/BUILD.gn @@ -27,4 +27,42 @@ mojom("mojom") { export_class_attribute_blink = "BLINK_PLATFORM_EXPORT" export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1" export_header_blink = "third_party/blink/public/platform/web_common.h" + + cpp_typemaps = [ + { + types = [ + { + mojom = "media_session.mojom.MediaImage" + cpp = "::media_session::MediaImage" + }, + { + mojom = "media_session.mojom.MediaImageBitmap" + cpp = "::SkBitmap" + nullable_is_same_type = true + }, + { + mojom = "media_session.mojom.MediaMetadata" + cpp = "::media_session::MediaMetadata" + }, + { + mojom = "media_session.mojom.MediaPosition" + cpp = "::media_session::MediaPosition" + }, + ] + traits_headers = + [ "//services/media_session/public/cpp/media_session_mojom_traits.h" ] + traits_sources = [ + "//services/media_session/public/cpp/media_session_mojom_traits.cc", + ] + traits_public_deps = [ + "//base", + "//services/media_session/public/cpp:base_cpp", + "//skia", + ] + traits_deps = [ + "//mojo/public/mojom/base", + "//ui/gfx/geometry/mojom", + ] + }, + ] } diff --git a/chromium/services/media_session/public/mojom/media_controller.mojom b/chromium/services/media_session/public/mojom/media_controller.mojom index 4e9e5f33191..72490b00268 100644 --- a/chromium/services/media_session/public/mojom/media_controller.mojom +++ b/chromium/services/media_session/public/mojom/media_controller.mojom @@ -83,6 +83,10 @@ interface MediaController { // Exit picture-in-picture. ExitPictureInPicture(); + + // Routes the audio from this Media Session to the given output device. If + // |id| is null, we will route to the default output device. + SetAudioSinkId(string? id); }; // The observer for observing media controller events. This is different to a diff --git a/chromium/services/media_session/public/mojom/media_session.mojom b/chromium/services/media_session/public/mojom/media_session.mojom index 53ba82e4fc9..63e95f72d78 100644 --- a/chromium/services/media_session/public/mojom/media_session.mojom +++ b/chromium/services/media_session/public/mojom/media_session.mojom @@ -9,7 +9,7 @@ import "mojo/public/mojom/base/time.mojom"; import "ui/gfx/geometry/mojom/geometry.mojom"; import "url/mojom/url.mojom"; -// Next MinVersion: 9 +// Next MinVersion: 10 [Extensible] enum MediaPlaybackState { @@ -146,6 +146,11 @@ struct MediaSessionInfo { // The audio/video state of the Media Session (if known). [MinVersion=8] MediaAudioVideoState audio_video_state; + + // The audio_sink_id tells the client the device_id of the audio output device + // being used for this media session. A null audio_sink_id implies that the + // default device is being used. + [MinVersion=9] string? audio_sink_id; }; // Contains debugging information about a MediaSession. This will be displayed @@ -188,7 +193,7 @@ interface MediaSessionObserver { // WebContents or ARC app. // TODO(https://crbug.com/875004): migrate media session from content/public // to mojo. -// Next Method ID: 17 +// Next Method ID: 18 interface MediaSession { [Extensible] enum SuspendType { @@ -270,4 +275,8 @@ interface MediaSession { // Exit picture-in-picture. ExitPictureInPicture@16(); + + // Routes the audio from this Media Session to the given output device. If + // |id| is null, we will route to the default output device. + SetAudioSinkId@17(string? id); }; diff --git a/chromium/services/metrics/public/cpp/ukm_source.cc b/chromium/services/metrics/public/cpp/ukm_source.cc index b83454bd115..fee7fe50ef2 100644 --- a/chromium/services/metrics/public/cpp/ukm_source.cc +++ b/chromium/services/metrics/public/cpp/ukm_source.cc @@ -53,6 +53,8 @@ SourceType ToProtobufSourceType(SourceIdType source_id_type) { return SourceType::PAYMENT_APP_ID; case SourceIdType::DESKTOP_WEB_APP_ID: return SourceType::DESKTOP_WEB_APP_ID; + case SourceIdType::WORKER_ID: + return SourceType::WORKER_ID; default: NOTREACHED(); return SourceType::DEFAULT; diff --git a/chromium/services/network/BUILD.gn b/chromium/services/network/BUILD.gn index 1a4135ea4b7..1941a52fd3f 100644 --- a/chromium/services/network/BUILD.gn +++ b/chromium/services/network/BUILD.gn @@ -2,13 +2,12 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//build/config/jumbo.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//net/features.gni") import("//services/network/public/cpp/features.gni") import("//testing/libfuzzer/fuzzer_test.gni") -jumbo_component("network_service") { +component("network_service") { sources = [ "chunked_data_pipe_upload_data_stream.cc", "chunked_data_pipe_upload_data_stream.h", @@ -30,8 +29,8 @@ jumbo_component("network_service") { "crash_keys.h", "crl_set_distributor.cc", "crl_set_distributor.h", - "cross_origin_read_blocking.cc", - "cross_origin_read_blocking.h", + "cross_origin_read_blocking_exception_for_plugin.cc", + "cross_origin_read_blocking_exception_for_plugin.h", "data_pipe_element_reader.cc", "data_pipe_element_reader.h", "dns_config_change_manager.cc", @@ -70,8 +69,6 @@ jumbo_component("network_service") { "network_quality_estimator_manager.h", "network_sandbox_hook_linux.cc", "network_sandbox_hook_linux.h", - "network_sandbox_win.cc", - "network_sandbox_win.h", "network_service.cc", "network_service.h", "network_service_network_delegate.cc", @@ -242,7 +239,6 @@ jumbo_component("network_service") { "//services/network/trust_tokens", "//services/service_manager/public/cpp", "//services/service_manager/public/mojom", - "//services/service_manager/sandbox:sandbox", "//third_party/webrtc_overrides:webrtc_component", "//url", ] @@ -253,14 +249,16 @@ jumbo_component("network_service") { sources += [ "expect_ct_reporter.cc", "expect_ct_reporter.h", + "sct_auditing_cache.cc", + "sct_auditing_cache.h", ] deps += [ "//components/certificate_transparency" ] } - if (is_linux) { + if (is_linux || is_chromeos) { deps += [ "//sandbox/linux:sandbox_services", - "//services/service_manager/sandbox:sandbox", + "//sandbox/policy", ] } @@ -288,7 +286,6 @@ source_set("tests") { "cors/cors_url_loader_factory_unittest.cc", "cors/cors_url_loader_unittest.cc", "cors/preflight_controller_unittest.cc", - "cross_origin_read_blocking_unittest.cc", "data_pipe_element_reader_unittest.cc", "dns_config_change_manager_unittest.cc", "host_resolver_unittest.cc", @@ -399,7 +396,10 @@ source_set("tests") { ] if (is_ct_supported) { - sources += [ "expect_ct_reporter_unittest.cc" ] + sources += [ + "expect_ct_reporter_unittest.cc", + "sct_auditing_cache_unittest.cc", + ] deps += [ "//components/certificate_transparency" ] } @@ -408,7 +408,7 @@ source_set("tests") { } } -jumbo_source_set("test_support") { +source_set("test_support") { testonly = true sources = [ @@ -440,8 +440,8 @@ jumbo_source_set("test_support") { "test/test_url_loader_factory.h", "test/test_utils.cc", "test/test_utils.h", - "udp_socket_test_util.cc", - "udp_socket_test_util.h", + "test/udp_socket_test_util.cc", + "test/udp_socket_test_util.h", ] if (is_chromeos) { diff --git a/chromium/services/network/DEPS b/chromium/services/network/DEPS index 2849a005c04..0d167b8cc46 100644 --- a/chromium/services/network/DEPS +++ b/chromium/services/network/DEPS @@ -17,7 +17,6 @@ include_rules = [ "+sandbox", "+services/proxy_resolver/public/mojom", "+services/service_manager/public", - "+services/service_manager/sandbox", "+third_party/boringssl/src/include", "+url", ] diff --git a/chromium/services/network/OWNERS b/chromium/services/network/OWNERS index 9a92f27fa95..42433e48786 100644 --- a/chromium/services/network/OWNERS +++ b/chromium/services/network/OWNERS @@ -30,7 +30,6 @@ per-file *_type_converter*.*=set noparent per-file *_type_converter*.*=file://ipc/SECURITY_OWNERS per-file network_sandbox_hook_linux.*.cc=file://sandbox/OWNERS -per-file network_sandbox_win.*=file://sandbox/win/OWNERS per-file network_quality*=file://net/nqe/OWNERS diff --git a/chromium/services/network/README.md b/chromium/services/network/README.md index d9f0ca04baf..9cbb5bf219c 100644 --- a/chromium/services/network/README.md +++ b/chromium/services/network/README.md @@ -27,6 +27,7 @@ https://docs.google.com/document/d/1wAHLw9h7gGuqJNCgG1mP1BmLtCGfZ2pys-PdZQ1vg7M/ # Related docs * [URLLoader](url_loader.md) +* [Slides describing the relationship with the fetch spec](https://docs.google.com/presentation/d/1r9KHuYbNlgqQ6UABAMiWz0ONTpSTnMaDJ8UeYZGWjls/) # Where does the network service run? diff --git a/chromium/services/network/chunked_data_pipe_upload_data_stream.cc b/chromium/services/network/chunked_data_pipe_upload_data_stream.cc index 305bfa2ef3c..65202276331 100644 --- a/chromium/services/network/chunked_data_pipe_upload_data_stream.cc +++ b/chromium/services/network/chunked_data_pipe_upload_data_stream.cc @@ -49,9 +49,14 @@ int ChunkedDataPipeUploadDataStream::InitInternal( return net::ERR_FAILED; // Get a new data pipe and start. - mojo::DataPipe data_pipe; - chunked_data_pipe_getter_->StartReading(std::move(data_pipe.producer_handle)); - data_pipe_ = std::move(data_pipe.consumer_handle); + mojo::ScopedDataPipeProducerHandle data_pipe_producer; + mojo::ScopedDataPipeConsumerHandle data_pipe_consumer; + MojoResult result = + mojo::CreateDataPipe(nullptr, &data_pipe_producer, &data_pipe_consumer); + if (result != MOJO_RESULT_OK) + return net::ERR_INSUFFICIENT_RESOURCES; + chunked_data_pipe_getter_->StartReading(std::move(data_pipe_producer)); + data_pipe_ = std::move(data_pipe_consumer); return net::OK; } diff --git a/chromium/services/network/cookie_manager_unittest.cc b/chromium/services/network/cookie_manager_unittest.cc index b66d553eb79..671c169a780 100644 --- a/chromium/services/network/cookie_manager_unittest.cc +++ b/chromium/services/network/cookie_manager_unittest.cc @@ -17,6 +17,7 @@ #include "base/test/bind_test_util.h" #include "base/test/task_environment.h" #include "base/time/time.h" +#include "net/cookies/cookie_access_result.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/cookie_monster.h" @@ -152,8 +153,8 @@ class SynchronousCookieManager { cookie, net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options, base::BindLambdaForTesting( - [&run_loop, &result_out](net::CookieInclusionStatus result) { - result_out = result; + [&run_loop, &result_out](net::CookieAccessResult result) { + result_out = result.status; run_loop.Quit(); })); @@ -161,7 +162,7 @@ class SynchronousCookieManager { return result_out.IsInclude(); } - net::CookieInclusionStatus SetCanonicalCookieWithStatus( + net::CookieAccessResult SetCanonicalCookieWithAccessResult( const net::CanonicalCookie& cookie, std::string source_scheme, bool modify_http_only) { @@ -171,13 +172,13 @@ class SynchronousCookieManager { net::CookieOptions::SameSiteCookieContext::MakeInclusive()); if (modify_http_only) options.set_include_httponly(); - net::CookieInclusionStatus result_out( - net::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR); + auto result_out = net::CookieAccessResult(net::CookieInclusionStatus( + net::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR)); cookie_service_->SetCanonicalCookie( cookie, net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options, base::BindLambdaForTesting( - [&run_loop, &result_out](net::CookieInclusionStatus result) { + [&run_loop, &result_out](net::CookieAccessResult result) { result_out = result; run_loop.Quit(); })); @@ -262,7 +263,7 @@ class CookieManagerTest : public testing::Test { bool SetCanonicalCookie(const net::CanonicalCookie& cookie, std::string source_scheme, bool can_modify_httponly) { - net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback; + net::ResultSavingCookieCallback<net::CookieAccessResult> callback; net::CookieOptions options; options.set_same_site_cookie_context( net::CookieOptions::SameSiteCookieContext::MakeInclusive()); @@ -272,11 +273,9 @@ class CookieManagerTest : public testing::Test { cookie_monster_->SetCanonicalCookieAsync( std::make_unique<net::CanonicalCookie>(cookie), net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options, - base::BindOnce( - &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, - base::Unretained(&callback))); + callback.MakeCallback()); callback.WaitUntilDone(); - return callback.result().IsInclude(); + return callback.result().status.IsInclude(); } std::string DumpAllCookies() { @@ -832,17 +831,19 @@ TEST_F(CookieManagerTest, DeleteThroughSet) { } TEST_F(CookieManagerTest, ConfirmSecureSetFails) { - EXPECT_TRUE( - service_wrapper() - ->SetCanonicalCookieWithStatus( - net::CanonicalCookie("N", "O", kCookieDomain, "/", base::Time(), - base::Time(), base::Time(), - /*secure=*/true, /*httponly=*/false, - net::CookieSameSite::NO_RESTRICTION, - net::COOKIE_PRIORITY_MEDIUM), - "http", false) - .HasExactlyExclusionReasonsForTesting( - {net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); + net::CookieAccessResult access_result = + service_wrapper()->SetCanonicalCookieWithAccessResult( + net::CanonicalCookie("N", "O", kCookieDomain, "/", base::Time(), + base::Time(), base::Time(), + /*secure=*/true, /*httponly=*/false, + net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_MEDIUM), + "http", false); + + EXPECT_TRUE(access_result.status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); + EXPECT_EQ(access_result.effective_same_site, + net::CookieEffectiveSameSite::NO_RESTRICTION); std::vector<net::CanonicalCookie> cookies = service_wrapper()->GetAllCookies(); @@ -850,17 +851,19 @@ TEST_F(CookieManagerTest, ConfirmSecureSetFails) { } TEST_F(CookieManagerTest, ConfirmHttpOnlySetFails) { - EXPECT_TRUE( - service_wrapper() - ->SetCanonicalCookieWithStatus( - net::CanonicalCookie("N", "O", kCookieDomain, "/", base::Time(), - base::Time(), base::Time(), - /*secure=*/false, /*httponly=*/true, - net::CookieSameSite::LAX_MODE, - net::COOKIE_PRIORITY_MEDIUM), - "http", false) - .HasExactlyExclusionReasonsForTesting( - {net::CookieInclusionStatus::EXCLUDE_HTTP_ONLY})); + net::CookieAccessResult access_result = + service_wrapper()->SetCanonicalCookieWithAccessResult( + net::CanonicalCookie("N", "O", kCookieDomain, "/", base::Time(), + base::Time(), base::Time(), + /*secure=*/false, /*httponly=*/true, + net::CookieSameSite::LAX_MODE, + net::COOKIE_PRIORITY_MEDIUM), + "http", false); + + EXPECT_TRUE(access_result.status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus::EXCLUDE_HTTP_ONLY})); + EXPECT_EQ(access_result.effective_same_site, + net::CookieEffectiveSameSite::LAX_MODE); std::vector<net::CanonicalCookie> cookies = service_wrapper()->GetAllCookies(); @@ -876,17 +879,19 @@ TEST_F(CookieManagerTest, ConfirmSecureOverwriteFails) { net::COOKIE_PRIORITY_MEDIUM), "https", true)); - EXPECT_TRUE( - service_wrapper() - ->SetCanonicalCookieWithStatus( - net::CanonicalCookie( - "Secure", "Nope", kCookieDomain, "/with/path", base::Time(), - base::Time(), base::Time(), /*secure=*/false, - /*httponly=*/false, net::CookieSameSite::LAX_MODE, - net::COOKIE_PRIORITY_MEDIUM), - "http", false) - .HasExactlyExclusionReasonsForTesting( - {net::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE})); + net::CookieAccessResult access_result = + service_wrapper()->SetCanonicalCookieWithAccessResult( + net::CanonicalCookie( + "Secure", "Nope", kCookieDomain, "/with/path", base::Time(), + base::Time(), base::Time(), /*secure=*/false, + /*httponly=*/false, net::CookieSameSite::LAX_MODE, + net::COOKIE_PRIORITY_MEDIUM), + "http", false); + + EXPECT_TRUE(access_result.status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE})); + EXPECT_EQ(access_result.effective_same_site, + net::CookieEffectiveSameSite::LAX_MODE); std::vector<net::CanonicalCookie> cookies = service_wrapper()->GetAllCookies(); @@ -905,17 +910,19 @@ TEST_F(CookieManagerTest, ConfirmHttpOnlyOverwriteFails) { net::COOKIE_PRIORITY_MEDIUM), "http", true)); - EXPECT_TRUE( - service_wrapper() - ->SetCanonicalCookieWithStatus( - net::CanonicalCookie( - "HttpOnly", "Nope", kCookieDomain, "/with/path", base::Time(), - base::Time(), base::Time(), /*secure=*/false, - /*httponly=*/false, net::CookieSameSite::LAX_MODE, - net::COOKIE_PRIORITY_MEDIUM), - "https", false) - .HasExactlyExclusionReasonsForTesting( - {net::CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY})); + net::CookieAccessResult access_result = + service_wrapper()->SetCanonicalCookieWithAccessResult( + net::CanonicalCookie( + "HttpOnly", "Nope", kCookieDomain, "/with/path", base::Time(), + base::Time(), base::Time(), /*secure=*/false, + /*httponly=*/false, net::CookieSameSite::LAX_MODE, + net::COOKIE_PRIORITY_MEDIUM), + "https", false); + + EXPECT_TRUE(access_result.status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY})); + EXPECT_EQ(access_result.effective_same_site, + net::CookieEffectiveSameSite::LAX_MODE); std::vector<net::CanonicalCookie> cookies = service_wrapper()->GetAllCookies(); diff --git a/chromium/services/network/cookie_settings.cc b/chromium/services/network/cookie_settings.cc index 5122960ccdd..581e395660e 100644 --- a/chromium/services/network/cookie_settings.cc +++ b/chromium/services/network/cookie_settings.cc @@ -7,6 +7,7 @@ #include <functional> #include "base/bind.h" +#include "base/callback.h" #include "base/strings/string_split.h" #include "components/content_settings/core/common/content_settings_utils.h" #include "net/base/net_errors.h" @@ -56,10 +57,10 @@ void CookieSettings::set_content_settings_for_legacy_cookie_access( AppendEmergencyLegacyCookieAccess(&settings_for_legacy_cookie_access_); } -SessionCleanupCookieStore::DeleteCookiePredicate -CookieSettings::CreateDeleteCookieOnExitPredicate() const { +DeleteCookiePredicate CookieSettings::CreateDeleteCookieOnExitPredicate() + const { if (!HasSessionOnlyOrigins()) - return SessionCleanupCookieStore::DeleteCookiePredicate(); + return DeleteCookiePredicate(); return base::BindRepeating(&CookieSettings::ShouldDeleteCookieOnExit, base::Unretained(this), std::cref(content_settings_)); diff --git a/chromium/services/network/cookie_settings.h b/chromium/services/network/cookie_settings.h index aab85a1a8c5..84eae422470 100644 --- a/chromium/services/network/cookie_settings.h +++ b/chromium/services/network/cookie_settings.h @@ -8,7 +8,7 @@ #include "base/component_export.h" #include "components/content_settings/core/common/content_settings.h" #include "components/content_settings/core/common/cookie_settings_base.h" -#include "services/network/session_cleanup_cookie_store.h" +#include "services/network/public/cpp/session_cookie_delete_predicate.h" class GURL; @@ -67,8 +67,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CookieSettings // Returns a predicate that takes the domain of a cookie and a bool whether // the cookie is secure and returns true if the cookie should be deleted on // exit. - SessionCleanupCookieStore::DeleteCookiePredicate - CreateDeleteCookieOnExitPredicate() const; + DeleteCookiePredicate CreateDeleteCookieOnExitPredicate() const; // content_settings::CookieSettingsBase: void GetSettingForLegacyCookieAccess(const std::string& cookie_domain, diff --git a/chromium/services/network/cors/cors_url_loader.cc b/chromium/services/network/cors/cors_url_loader.cc index 2e7f769c7a3..56f851fdb16 100644 --- a/chromium/services/network/cors/cors_url_loader.cc +++ b/chromium/services/network/cors/cors_url_loader.cc @@ -28,21 +28,7 @@ namespace cors { namespace { -// These values are persisted to logs. Entries should not be renumbered and -// numeric values should never be reused. -enum class CompletionStatusMetric { - kPassedWhenCorsFlagUnset = 0, - kFailedWhenCorsFlagUnset = 1, - kPassedWhenCorsFlagSet = 2, - kFailedWhenCorsFlagSet = 3, - kBlockedByCors = 4, - - kMaxValue = kBlockedByCors, -}; - -bool NeedsPreflight( - const ResourceRequest& request, - const base::flat_set<std::string>& extra_safelisted_header_names) { +bool NeedsPreflight(const ResourceRequest& request) { if (!IsCorsEnabledRequestMode(request.mode)) return false; @@ -62,26 +48,10 @@ bool NeedsPreflight( return true; return !CorsUnsafeNotForbiddenRequestHeaderNames( - request.headers.GetHeaderVector(), request.is_revalidating, - extra_safelisted_header_names) + request.headers.GetHeaderVector(), request.is_revalidating) .empty(); } -void ReportCompletionStatusMetric(bool fetch_cors_flag, - const URLLoaderCompletionStatus& status) { - CompletionStatusMetric metric; - if (status.error_code == net::OK) { - metric = fetch_cors_flag ? CompletionStatusMetric::kPassedWhenCorsFlagSet - : CompletionStatusMetric::kPassedWhenCorsFlagUnset; - } else if (status.cors_error_status) { - metric = CompletionStatusMetric::kBlockedByCors; - } else { - metric = fetch_cors_flag ? CompletionStatusMetric::kFailedWhenCorsFlagSet - : CompletionStatusMetric::kFailedWhenCorsFlagUnset; - } - UMA_HISTOGRAM_ENUMERATION("Net.Cors.CompletionStatus", metric); -} - constexpr const char kTimingAllowOrigin[] = "Timing-Allow-Origin"; } // namespace @@ -250,9 +220,7 @@ void CorsURLLoader::FollowRedirect( // // After both OOR-CORS and network service are fully shipped, we may be able // to remove the logic in net/. - if ((fetch_cors_flag_ && - NeedsPreflight( - request_, preflight_controller_->extra_safelisted_header_names())) || + if ((fetch_cors_flag_ && NeedsPreflight(request_)) || (!original_fetch_cors_flag && fetch_cors_flag_) || (fetch_cors_flag_ && original_method != request_.method)) { DCHECK_NE(request_.mode, mojom::RequestMode::kNoCors); @@ -388,14 +356,20 @@ void CorsURLLoader::OnReceiveRedirect(const net::RedirectInfo& redirect_info, tainted_ = true; } - // TODO(yhirano): Implement the following: + // TODO(crbug.com/1073353): Implement the following: // If either |actualResponse|’s status is 301 or 302 and |request|’s method is // `POST`, or |actualResponse|’s status is 303, set |request|’s method to - // `GET` and request’s body to null. - - // TODO(yhirano): Implement the following: + // `GET` and request’s body to null, and remove request-body-header name from + // request's headers. Some of them are implemented in //net, but when we + // create another request on exceptional redirect cases, such newly created + // request doesn't reflect the spec comformant request modifications. See the + // linked crbug for details. See also 4.4. HTTP-redirect fetch + // (https://fetch.spec.whatwg.org/#http-redirect-fetch), step 11. + + // TODO(crbug.com/1073353): Implement the following: // Invoke |set request’s referrer policy on redirect| on |request| and - // |actualResponse|. + // |actualResponse|. See 4.4. HTTP-redirect fetch + // (https://fetch.spec.whatwg.org/#http-redirect-fetch), step 14. redirect_info_ = redirect_info; @@ -510,9 +484,7 @@ void CorsURLLoader::StartRequest() { // Note that even when |NeedsPreflight(request_)| holds we don't make a // preflight request when |fetch_cors_flag_| is false (e.g., when the origin // of the url is equal to the origin of the request. - if (!fetch_cors_flag_ || - !NeedsPreflight(request_, - preflight_controller_->extra_safelisted_header_names())) { + if (!fetch_cors_flag_ || !NeedsPreflight(request_)) { StartNetworkRequest(net::OK, base::nullopt); return; } @@ -566,7 +538,6 @@ void CorsURLLoader::StartNetworkRequest( } void CorsURLLoader::HandleComplete(const URLLoaderCompletionStatus& status) { - ReportCompletionStatusMetric(fetch_cors_flag_, status); forwarding_client_->OnComplete(status); std::move(delete_callback_).Run(this); // |this| is deleted here. diff --git a/chromium/services/network/cors/cors_url_loader_factory.cc b/chromium/services/network/cors/cors_url_loader_factory.cc index 9c73e446778..a33206ae639 100644 --- a/chromium/services/network/cors/cors_url_loader_factory.cc +++ b/chromium/services/network/cors/cors_url_loader_factory.cc @@ -7,19 +7,23 @@ #include <utility> #include "base/bind.h" +#include "base/debug/dump_without_crashing.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" +#include "base/util/type_safety/strong_alias.h" #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/base/load_flags.h" +#include "net/http/http_util.h" #include "services/network/cors/cors_url_loader.h" #include "services/network/cors/preflight_controller.h" #include "services/network/crash_keys.h" -#include "services/network/cross_origin_read_blocking.h" +#include "services/network/cross_origin_read_blocking_exception_for_plugin.h" #include "services/network/network_context.h" #include "services/network/network_service.h" #include "services/network/public/cpp/cors/cors.h" +#include "services/network/public/cpp/cross_origin_read_blocking.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/header_util.h" #include "services/network/public/cpp/initiator_lock_compatibility.h" @@ -39,6 +43,22 @@ namespace cors { namespace { +using IsConsistent = ::util::StrongAlias<class IsConsistentTag, bool>; + +// Record, for requests with associated Trust Tokens operations of operation +// types requiring initiators to have the Trust Tokens Feature Policy feature +// enabled, whether the browser process thinks it's possible for the initiating +// frame to have the feature enabled. (If the answer is "no," it indicates +// a misbehaving renderer in theory but, unfortunately, is more likely a false +// positive due to inconsistent state; see crbug.com/1117458.) +void HistogramWhetherTrustTokenFeaturePolicyConsistentWithBrowserOpinion( + IsConsistent is_consistent) { + UMA_HISTOGRAM_BOOLEAN( + "Net.TrustTokens.SubresourceOperationRequiringFeaturePolicy." + "PolicyIsConsistentWithBrowserOpinion", + is_consistent.value()); +} + // Verifies state that should hold for Trust Tokens parameters provided by a // functioning renderer: // - Trust Tokens should be enabled @@ -64,24 +84,22 @@ bool VerifyTrustTokenParamsIntegrityIfPresent( return false; } - if (url_request.request_initiator && - !IsOriginPotentiallyTrustworthy(*url_request.request_initiator)) { - // Got a request with Trust Tokens parameters from an insecure context, - // but Trust Tokens operations may only be executed from secure - // contexts. - mojo::ReportBadMessage("TrustTokenParamsIntegrity: NotFromSecureContext"); - return false; - } - if (trust_token_redemption_policy == mojom::TrustTokenRedemptionPolicy::kForbid && DoesTrustTokenOperationRequireFeaturePolicy( url_request.trust_token_params->type)) { // Got a request configured for Trust Tokens redemption or signing from // a context in which this operation is prohibited. - mojo::ReportBadMessage( - "TrustTokenParamsIntegrity: MissingRequiredFeaturePolicy"); - return false; + // + // TODO(crbug.com/1118183): Re-add a ReportBadMessage (and false return) + // once the false positives in crbug.com/1117458 have been resolved. + base::debug::DumpWithoutCrashing(); + HistogramWhetherTrustTokenFeaturePolicyConsistentWithBrowserOpinion( + IsConsistent(false)); + } else if (DoesTrustTokenOperationRequireFeaturePolicy( + url_request.trust_token_params->type)) { + HistogramWhetherTrustTokenFeaturePolicyConsistentWithBrowserOpinion( + IsConsistent(true)); } return true; @@ -166,7 +184,7 @@ CorsURLLoaderFactory::CorsURLLoaderFactory( is_trusted_(params->is_trusted), disable_web_security_(params->disable_web_security), process_id_(params->process_id), - request_initiator_site_lock_(params->request_initiator_site_lock), + request_initiator_origin_lock_(params->request_initiator_origin_lock), ignore_isolated_world_origin_(params->ignore_isolated_world_origin), trust_token_redemption_policy_(params->trust_token_redemption_policy), isolation_info_(params->isolation_info), @@ -374,10 +392,11 @@ bool CorsURLLoaderFactory::IsValidRequest(const ResourceRequest& request, } } - // Compare |request_initiator| and |request_initiator_site_lock_|. + // Compare |request_initiator| and |request_initiator_origin_lock_|. InitiatorLockCompatibility initiator_lock_compatibility = - VerifyRequestInitiatorLockWithPluginCheck( - process_id_, request_initiator_site_lock_, request.request_initiator); + VerifyRequestInitiatorLockWithPluginCheck(process_id_, + request_initiator_origin_lock_, + request.request_initiator); UMA_HISTOGRAM_ENUMERATION( "NetworkService.URLLoader.RequestInitiatorOriginLockCompatibility", initiator_lock_compatibility); @@ -389,11 +408,13 @@ bool CorsURLLoaderFactory::IsValidRequest(const ResourceRequest& request, break; case InitiatorLockCompatibility::kNoLock: - // TODO(lukasza): https://crbug.com/891872: Browser process should always - // specify the request_initiator_site_lock in URLLoaderFactories given to - // a renderer process. Once https://crbug.com/891872 is fixed, the case - // below should return |false| (i.e. = bad message). - break; + // |request_initiator_origin_lock| should always be set in a + // URLLoaderFactory vended to a renderer process. See also + // https://crbug.com/1114906. + NOTREACHED(); + mojo::ReportBadMessage( + "CorsURLLoaderFactory: no initiator lock in a renderer request"); + return false; case InitiatorLockCompatibility::kNoInitiator: // Requests from the renderer need to always specify an initiator. @@ -408,8 +429,8 @@ bool CorsURLLoaderFactory::IsValidRequest(const ResourceRequest& request, if (base::FeatureList::IsEnabled( features::kRequestInitiatorSiteLockEnfocement)) { url::debug::ScopedOriginCrashKey initiator_lock_crash_key( - debug::GetRequestInitiatorSiteLockCrashKey(), - base::OptionalOrNullptr(request_initiator_site_lock_)); + debug::GetRequestInitiatorOriginLockCrashKey(), + base::OptionalOrNullptr(request_initiator_origin_lock_)); mojo::ReportBadMessage( "CorsURLLoaderFactory: lock VS initiator mismatch"); return false; @@ -468,6 +489,13 @@ bool CorsURLLoaderFactory::IsValidRequest(const ResourceRequest& request, return false; } + if (!net::HttpUtil::IsToken(request.method)) { + // Callers are expected to ensure that |method| follows RFC 7230. + mojo::ReportBadMessage( + "CorsURLLoaderFactory: invalid characters in method"); + return false; + } + // TODO(yhirano): If the request mode is "no-cors", the redirect mode should // be "follow". return true; @@ -476,16 +504,17 @@ bool CorsURLLoaderFactory::IsValidRequest(const ResourceRequest& request, InitiatorLockCompatibility CorsURLLoaderFactory::VerifyRequestInitiatorLockWithPluginCheck( uint32_t process_id, - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, const base::Optional<url::Origin>& request_initiator) { if (process_id == mojom::kBrowserProcessId) return InitiatorLockCompatibility::kBrowserProcess; InitiatorLockCompatibility result = VerifyRequestInitiatorLock( - request_initiator_site_lock, request_initiator); + request_initiator_origin_lock, request_initiator); if (result == InitiatorLockCompatibility::kIncorrectLock && - CrossOriginReadBlocking::ShouldAllowForPlugin(process_id)) { + CrossOriginReadBlockingExceptionForPlugin::ShouldAllowForPlugin( + process_id)) { result = InitiatorLockCompatibility::kExcludedCorbForPlugin; } diff --git a/chromium/services/network/cors/cors_url_loader_factory.h b/chromium/services/network/cors/cors_url_loader_factory.h index 1dfdeac49f8..86074799b74 100644 --- a/chromium/services/network/cors/cors_url_loader_factory.h +++ b/chromium/services/network/cors/cors_url_loader_factory.h @@ -87,7 +87,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CorsURLLoaderFactory final InitiatorLockCompatibility VerifyRequestInitiatorLockWithPluginCheck( uint32_t process_id, - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, const base::Optional<url::Origin>& request_initiator); bool GetAllowAnyCorsExemptHeaderForBrowser() const; @@ -105,7 +105,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CorsURLLoaderFactory final // Retained from URLLoaderFactoryParams: const bool disable_web_security_; const int32_t process_id_ = mojom::kInvalidProcessId; - const base::Optional<url::Origin> request_initiator_site_lock_; + const base::Optional<url::Origin> request_initiator_origin_lock_; const bool ignore_isolated_world_origin_; const mojom::TrustTokenRedemptionPolicy trust_token_redemption_policy_; net::IsolationInfo isolation_info_; diff --git a/chromium/services/network/cors/cors_url_loader_factory_unittest.cc b/chromium/services/network/cors/cors_url_loader_factory_unittest.cc index 20e261e4a92..52d64b36c4c 100644 --- a/chromium/services/network/cors/cors_url_loader_factory_unittest.cc +++ b/chromium/services/network/cors/cors_url_loader_factory_unittest.cc @@ -70,6 +70,8 @@ class CorsURLLoaderFactoryTest : public testing::Test { auto factory_params = network::mojom::URLLoaderFactoryParams::New(); factory_params->process_id = kProcessId; + factory_params->request_initiator_origin_lock = + url::Origin::Create(GURL("http://localhost")); auto resource_scheduler_client = base::MakeRefCounted<ResourceSchedulerClient>( kProcessId, kRouteId, &resource_scheduler_, diff --git a/chromium/services/network/cors/cors_url_loader_unittest.cc b/chromium/services/network/cors/cors_url_loader_unittest.cc index 3d64f7ef8c1..191d3b0480d 100644 --- a/chromium/services/network/cors/cors_url_loader_unittest.cc +++ b/chromium/services/network/cors/cors_url_loader_unittest.cc @@ -27,7 +27,7 @@ #include "net/http/http_request_headers.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" -#include "net/url_request/url_request.h" +#include "net/url_request/referrer_policy.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" #include "services/network/cors/cors_url_loader_factory.h" @@ -146,7 +146,7 @@ class TestURLLoaderFactory : public mojom::URLLoaderFactory { class CorsURLLoaderTest : public testing::Test { public: - using ReferrerPolicy = net::URLRequest::ReferrerPolicy; + using ReferrerPolicy = net::ReferrerPolicy; CorsURLLoaderTest() : task_environment_(base::test::TaskEnvironment::MainThreadType::IO) { @@ -179,7 +179,9 @@ class CorsURLLoaderTest : public testing::Test { network_context_remote_.BindNewPipeAndPassReceiver(), std::move(context_params)); - ResetFactory(base::nullopt, kRendererProcessId); + const url::Origin default_initiator_origin = + url::Origin::Create(GURL("https://example.com")); + ResetFactory(default_initiator_origin, kRendererProcessId); } void SetUp() override { SetUp(mojom::NetworkContextParams::New()); } @@ -333,7 +335,7 @@ class CorsURLLoaderTest : public testing::Test { base::StringPiece method, const GURL& url, base::StringPiece referrer = base::StringPiece(), - ReferrerPolicy referrer_policy = net::URLRequest::NO_REFERRER, + ReferrerPolicy referrer_policy = net::ReferrerPolicy::NO_REFERRER, net::SiteForCookies site_for_cookies = net::SiteForCookies()) { net::RedirectInfo redirect_info; redirect_info.status_code = status_code; @@ -350,6 +352,9 @@ class CorsURLLoaderTest : public testing::Test { bool is_trusted, bool ignore_isolated_world_origin, bool skip_cors_enabled_scheme_check) { + if (process_id != mojom::kBrowserProcessId) + DCHECK(initiator.has_value()); + test_url_loader_factory_ = std::make_unique<TestURLLoaderFactory>(); test_url_loader_factory_receiver_ = std::make_unique<mojo::Receiver<mojom::URLLoaderFactory>>( @@ -357,7 +362,7 @@ class CorsURLLoaderTest : public testing::Test { auto factory_params = network::mojom::URLLoaderFactoryParams::New(); if (initiator) { - factory_params->request_initiator_site_lock = *initiator; + factory_params->request_initiator_origin_lock = *initiator; if (!initiator->opaque()) { factory_params->factory_bound_access_patterns = network::mojom::CorsOriginAccessPatterns::New(); @@ -466,11 +471,33 @@ class BadMessageTestHelper { DISALLOW_COPY_AND_ASSIGN(BadMessageTestHelper); }; +TEST_F(CorsURLLoaderTest, NoCorsWithInvalidMethod) { + ResourceRequest request; + request.mode = mojom::RequestMode::kNoCors; + request.credentials_mode = mojom::CredentialsMode::kInclude; + request.url = GURL("https://example.com/"); + request.request_initiator = url::Origin::Create(request.url); + request.method = "GET\r\nHost: other.example.com"; + + BadMessageTestHelper bad_message_helper; + CreateLoaderAndStart(request); + RunUntilComplete(); + + EXPECT_FALSE(IsNetworkLoaderStarted()); + EXPECT_FALSE(client().has_received_redirect()); + EXPECT_FALSE(client().has_received_response()); + EXPECT_TRUE(client().has_received_completion()); + EXPECT_EQ(net::ERR_INVALID_ARGUMENT, client().completion_status().error_code); + EXPECT_THAT(bad_message_helper.bad_message_reports(), + ::testing::ElementsAre( + "CorsURLLoaderFactory: invalid characters in method")); +} + TEST_F(CorsURLLoaderTest, SameOriginWithoutInitiator) { ResourceRequest request; request.mode = mojom::RequestMode::kSameOrigin; request.credentials_mode = mojom::CredentialsMode::kInclude; - request.url = GURL("http://example.com/"); + request.url = GURL("https://example.com/"); request.request_initiator = base::nullopt; BadMessageTestHelper bad_message_helper; @@ -488,10 +515,17 @@ TEST_F(CorsURLLoaderTest, SameOriginWithoutInitiator) { } TEST_F(CorsURLLoaderTest, NoCorsWithoutInitiator) { + // This test needs to simulate a factory used from the browser process, + // because only the browser process may start requests with no + // |request_initiator|. A renderer process would have run into NOTREACHED and + // mojo::ReportBadMessage via InitiatorLockCompatibility::kNoInitiator case in + // CorsURLLoaderFactory::IsValidRequest. + ResetFactory(base::nullopt /* initiator */, mojom::kBrowserProcessId); + ResourceRequest request; request.mode = mojom::RequestMode::kNoCors; request.credentials_mode = mojom::CredentialsMode::kInclude; - request.url = GURL("http://example.com/"); + request.url = GURL("https://example.com/"); request.request_initiator = base::nullopt; CreateLoaderAndStart(request); @@ -511,7 +545,7 @@ TEST_F(CorsURLLoaderTest, CorsWithoutInitiator) { ResourceRequest request; request.mode = mojom::RequestMode::kCors; request.credentials_mode = mojom::CredentialsMode::kInclude; - request.url = GURL("http://example.com/"); + request.url = GURL("https://example.com/"); request.request_initiator = base::nullopt; BadMessageTestHelper bad_message_helper; @@ -534,7 +568,7 @@ TEST_F(CorsURLLoaderTest, NavigateWithoutInitiator) { ResourceRequest request; request.mode = mojom::RequestMode::kNavigate; request.credentials_mode = mojom::CredentialsMode::kInclude; - request.url = GURL("http://example.com/"); + request.url = GURL("https://example.com/"); request.request_initiator = base::nullopt; CreateLoaderAndStart(request); @@ -553,7 +587,7 @@ TEST_F(CorsURLLoaderTest, NavigateWithoutInitiator) { TEST_F(CorsURLLoaderTest, NavigationFromRenderer) { ResourceRequest request; request.mode = mojom::RequestMode::kNavigate; - request.url = GURL("http://example.com/"); + request.url = GURL("https://example.com/"); request.request_initiator = base::nullopt; BadMessageTestHelper bad_message_helper; @@ -571,7 +605,7 @@ TEST_F(CorsURLLoaderTest, NavigationFromRenderer) { } TEST_F(CorsURLLoaderTest, SameOriginRequest) { - const GURL url("http://example.com/foo.png"); + const GURL url("https://example.com/foo.png"); CreateLoaderAndStart(url.GetOrigin(), url, mojom::RequestMode::kSameOrigin); RunUntilCreateLoaderAndStartCalled(); @@ -588,8 +622,8 @@ TEST_F(CorsURLLoaderTest, SameOriginRequest) { } TEST_F(CorsURLLoaderTest, CrossOriginRequestWithNoCorsMode) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); CreateLoaderAndStart(origin, url, mojom::RequestMode::kNoCors); RunUntilCreateLoaderAndStartCalled(); @@ -608,8 +642,8 @@ TEST_F(CorsURLLoaderTest, CrossOriginRequestWithNoCorsMode) { } TEST_F(CorsURLLoaderTest, CrossOriginRequestWithNoCorsModeAndPatchMethod) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); ResourceRequest request; request.mode = mojom::RequestMode::kNoCors; request.credentials_mode = mojom::CredentialsMode::kInclude; @@ -632,12 +666,12 @@ TEST_F(CorsURLLoaderTest, CrossOriginRequestWithNoCorsModeAndPatchMethod) { std::string origin_header; EXPECT_TRUE(GetRequest().headers.GetHeader(net::HttpRequestHeaders::kOrigin, &origin_header)); - EXPECT_EQ(origin_header, "http://example.com"); + EXPECT_EQ(origin_header, "https://example.com"); } TEST_F(CorsURLLoaderTest, CrossOriginRequestFetchRequestModeSameOrigin) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); CreateLoaderAndStart(origin, url, mojom::RequestMode::kSameOrigin); RunUntilComplete(); @@ -654,8 +688,8 @@ TEST_F(CorsURLLoaderTest, CrossOriginRequestFetchRequestModeSameOrigin) { } TEST_F(CorsURLLoaderTest, CrossOriginRequestWithCorsModeButMissingCorsHeader) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); CreateLoaderAndStart(origin, url, mojom::RequestMode::kCors); RunUntilCreateLoaderAndStartCalled(); @@ -668,7 +702,7 @@ TEST_F(CorsURLLoaderTest, CrossOriginRequestWithCorsModeButMissingCorsHeader) { std::string origin_header; EXPECT_TRUE(GetRequest().headers.GetHeader(net::HttpRequestHeaders::kOrigin, &origin_header)); - EXPECT_EQ(origin_header, "http://example.com"); + EXPECT_EQ(origin_header, "https://example.com"); EXPECT_FALSE(client().has_received_redirect()); EXPECT_FALSE(client().has_received_response()); EXPECT_EQ(net::ERR_FAILED, client().completion_status().error_code); @@ -678,13 +712,13 @@ TEST_F(CorsURLLoaderTest, CrossOriginRequestWithCorsModeButMissingCorsHeader) { } TEST_F(CorsURLLoaderTest, CrossOriginRequestWithCorsMode) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); CreateLoaderAndStart(origin, url, mojom::RequestMode::kCors); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnReceiveResponse( - {{"Access-Control-Allow-Origin", "http://example.com"}}); + {{"Access-Control-Allow-Origin", "https://example.com"}}); NotifyLoaderClientOnComplete(net::OK); RunUntilComplete(); @@ -698,8 +732,8 @@ TEST_F(CorsURLLoaderTest, CrossOriginRequestWithCorsMode) { TEST_F(CorsURLLoaderTest, CrossOriginRequestFetchRequestWithCorsModeButMismatchedCorsHeader) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); CreateLoaderAndStart(origin, url, mojom::RequestMode::kCors); RunUntilCreateLoaderAndStartCalled(); @@ -771,14 +805,14 @@ TEST_F(CorsURLLoaderTest, CorsEnabledSameCustomSchemeRequest) { } TEST_F(CorsURLLoaderTest, StripUsernameAndPassword) { - const GURL origin("http://example.com"); - const GURL url("http://foo:bar@other.com/foo.png"); - std::string stripped_url = "http://other.com/foo.png"; + const GURL origin("https://example.com"); + const GURL url("http://foo:bar@other.example.com/foo.png"); + std::string stripped_url = "http://other.example.com/foo.png"; CreateLoaderAndStart(origin, url, mojom::RequestMode::kCors); RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnReceiveResponse( - {{"Access-Control-Allow-Origin", "http://example.com"}}); + {{"Access-Control-Allow-Origin", "https://example.com"}}); NotifyLoaderClientOnComplete(net::OK); RunUntilComplete(); @@ -1158,7 +1192,7 @@ TEST_F(CorsURLLoaderTest, RedirectInfoShouldBeUsed) { request.request_initiator = url::Origin::Create(origin); request.referrer = url; request.referrer_policy = - net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; + net::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); @@ -1166,12 +1200,12 @@ TEST_F(CorsURLLoaderTest, RedirectInfoShouldBeUsed) { EXPECT_EQ(url, GetRequest().url); EXPECT_EQ("POST", GetRequest().method); EXPECT_EQ(url, GetRequest().referrer); - EXPECT_EQ(net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, + EXPECT_EQ(net::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, GetRequest().referrer_policy); NotifyLoaderClientOnReceiveRedirect(CreateRedirectInfo( 303, "GET", new_url, "https://other.example.com", - net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN)); + net::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN)); RunUntilRedirectReceived(); EXPECT_TRUE(IsNetworkLoaderStarted()); @@ -1187,7 +1221,7 @@ TEST_F(CorsURLLoaderTest, RedirectInfoShouldBeUsed) { EXPECT_EQ(new_url, GetRequest().url); EXPECT_EQ("GET", GetRequest().method); EXPECT_EQ(GURL("https://other.example.com"), GetRequest().referrer); - EXPECT_EQ(net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, + EXPECT_EQ(net::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, GetRequest().referrer_policy); NotifyLoaderClientOnReceiveResponse( @@ -1207,11 +1241,6 @@ TEST_F(CorsURLLoaderTest, RedirectInfoShouldBeUsed) { // assumptions about whether these need to be updated in CorsURLLoader. TEST_F(CorsURLLoaderTest, InterceptedRedirectChangesIsolationInfoAndSiteForCookies) { - auto params = network::mojom::URLLoaderFactoryParams::New(); - ResetFactory(base::nullopt, kRendererProcessId, true /* is_trusted */, - params->ignore_isolated_world_origin, - false /* skip_cors_enabled_scheme_check */); - const GURL url("https://example.com/foo.png"); const url::Origin url_origin = url::Origin::Create(url); const net::SiteForCookies url_site_for_cookies = @@ -1222,6 +1251,11 @@ TEST_F(CorsURLLoaderTest, const net::SiteForCookies new_url_site_for_cookies = net::SiteForCookies::FromOrigin(new_url_origin); + auto params = network::mojom::URLLoaderFactoryParams::New(); + ResetFactory(url_origin, kRendererProcessId, true /* is_trusted */, + params->ignore_isolated_world_origin, + false /* skip_cors_enabled_scheme_check */); + ResourceRequest request; request.mode = mojom::RequestMode::kCors; request.credentials_mode = mojom::CredentialsMode::kOmit; @@ -1241,7 +1275,7 @@ TEST_F(CorsURLLoaderTest, EXPECT_EQ(url, GetRequest().url); NotifyLoaderClientOnReceiveRedirect(CreateRedirectInfo( - 303, "GET", new_url, "" /* referrer */, net::URLRequest::NO_REFERRER, + 303, "GET", new_url, "" /* referrer */, net::ReferrerPolicy::NO_REFERRER, new_url_site_for_cookies)); RunUntilRedirectReceived(); @@ -1349,8 +1383,8 @@ TEST_F(CorsURLLoaderTest, CorsExemptHeaderRemovalOnCrossOriginRedirects) { RunUntilCreateLoaderAndStartCalled(); EXPECT_EQ(1, num_created_loaders()); - NotifyLoaderClientOnReceiveRedirect( - CreateRedirectInfo(301, "GET", GURL("https://google.com/bar.png"))); + NotifyLoaderClientOnReceiveRedirect(CreateRedirectInfo( + 301, "GET", GURL("https://other.example.com/bar.png"))); RunUntilRedirectReceived(); EXPECT_TRUE(IsNetworkLoaderStarted()); @@ -1371,6 +1405,7 @@ TEST_F(CorsURLLoaderTest, CorsExemptHeaderRemovalOnCrossOriginRedirects) { TEST_F(CorsURLLoaderTest, CorsExemptHeaderModificationOnRedirects) { ResourceRequest request; request.url = GURL("https://example.com/foo.png"); + request.request_initiator = url::Origin::Create(request.url); request.cors_exempt_headers.SetHeader(kTestCorsExemptHeader, "test-value"); CreateLoaderAndStart(request); RunUntilCreateLoaderAndStartCalled(); @@ -1404,8 +1439,8 @@ TEST_F(CorsURLLoaderTest, CorsExemptHeaderModificationOnRedirects) { // OriginAccessListTest, but this test intends to verify if CorsURlLoader calls // the list properly. TEST_F(CorsURLLoaderTest, OriginAccessList_Allowed) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); // Adds an entry to allow the cross origin request beyond the CORS // rules. @@ -1434,10 +1469,10 @@ TEST_F(CorsURLLoaderTest, OriginAccessList_Allowed) { // ResourceRequest::isolated_world_origin when consulting OriginAccessList. TEST_F(CorsURLLoaderTest, OriginAccessList_IsolatedWorldOrigin) { const url::Origin main_world_origin = - url::Origin::Create(GURL("http://main-world.com")); + url::Origin::Create(GURL("http://main-world.example.com")); const url::Origin isolated_world_origin = - url::Origin::Create(GURL("http://isolated-world.com")); - const GURL url("http://other.com/foo.png"); + url::Origin::Create(GURL("http://isolated-world.example.com")); + const GURL url("http://other.example.com/foo.png"); ResetFactory(main_world_origin, kRendererProcessId, false /* trusted */, false /* ignore_isolated_world_origin */, @@ -1475,13 +1510,13 @@ TEST_F(CorsURLLoaderTest, OriginAccessList_IsolatedWorldOrigin) { // after redirects. TEST_F(CorsURLLoaderTest, OriginAccessList_IsolatedWorldOrigin_Redirect) { const url::Origin main_world_origin = - url::Origin::Create(GURL("http://main-world.com")); + url::Origin::Create(GURL("http://main-world.example.com")); const url::Origin isolated_world_origin = - url::Origin::Create(GURL("http://isolated-world.com")); - const GURL url("http://other.com/foo.png"); + url::Origin::Create(GURL("http://isolated-world.example.com")); + const GURL url("http://other.example.com/foo.png"); // |new_url| is same-origin as |url| to avoid tainting the response // in CorsURLLoader::OnReceiveRedirect. - const GURL new_url("http://other.com/bar.png"); + const GURL new_url("http://other.example.com/bar.png"); ResetFactory(main_world_origin, kRendererProcessId, false /* trusted */, false /* ignore_isolated_world_origin */, @@ -1532,10 +1567,10 @@ TEST_F(CorsURLLoaderTest, OriginAccessList_IsolatedWorldOrigin_Redirect) { // when URLLoaderFactoryParams::ignore_isolated_world_origin is set to true. TEST_F(CorsURLLoaderTest, OriginAccessList_IsolatedWorldOriginIgnored) { const url::Origin main_world_origin = - url::Origin::Create(GURL("http://main-world.com")); + url::Origin::Create(GURL("http://main-world.example.com")); const url::Origin isolated_world_origin = - url::Origin::Create(GURL("http://isolated-world.com")); - const GURL url("http://other.com/foo.png"); + url::Origin::Create(GURL("http://isolated-world.example.com")); + const GURL url("http://other.example.com/foo.png"); ResetFactory(main_world_origin, kRendererProcessId, false /* trusted */, true /* ignore_isolated_world_origin */, @@ -1568,8 +1603,8 @@ TEST_F(CorsURLLoaderTest, OriginAccessList_IsolatedWorldOriginIgnored) { // Check if higher-priority block list wins. TEST_F(CorsURLLoaderTest, OriginAccessList_Blocked) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); AddAllowListEntryForOrigin(url::Origin::Create(origin), url.scheme(), url.host(), @@ -1596,8 +1631,8 @@ TEST_F(CorsURLLoaderTest, OriginAccessList_Blocked) { // per-URLLoaderFactory list. This test verifies if per-URLLoaderFactory list // works. TEST_F(CorsURLLoaderTest, OriginAccessList_AllowedByFactoryList) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); AddFactoryBoundAllowListEntryForOrigin( url::Origin::Create(origin), url.scheme(), url.host(), @@ -1622,8 +1657,8 @@ TEST_F(CorsURLLoaderTest, OriginAccessList_AllowedByFactoryList) { // Checks if CorsURLLoader can respect the per-NetworkContext block list. TEST_F(CorsURLLoaderTest, OriginAccessList_AllowedByFactoryListButBlocked) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); AddFactoryBoundAllowListEntryForOrigin( url::Origin::Create(origin), url.scheme(), url.host(), @@ -1648,8 +1683,8 @@ TEST_F(CorsURLLoaderTest, OriginAccessList_AllowedByFactoryListButBlocked) { // Tests if OriginAccessList is actually used to decide response tainting. TEST_F(CorsURLLoaderTest, OriginAccessList_NoCors) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); // Adds an entry to allow the cross origin request without using // CORS. @@ -1675,8 +1710,8 @@ TEST_F(CorsURLLoaderTest, OriginAccessList_NoCors) { } TEST_F(CorsURLLoaderTest, OriginAccessList_POST) { - const GURL origin("http://example.com"); - const GURL url("http://other.com/foo.png"); + const GURL origin("https://example.com"); + const GURL url("http://other.example.com/foo.png"); // Adds an entry to allow the cross origin request beyond the CORS // rules. @@ -1943,9 +1978,10 @@ TEST_F(CorsURLLoaderTest, RequestWithHostHeaderFails) { request.mode = mojom::RequestMode::kCors; request.credentials_mode = mojom::CredentialsMode::kOmit; request.method = net::HttpRequestHeaders::kGetMethod; - request.url = GURL("https://foo.test/path"); - request.request_initiator = url::Origin::Create(GURL("https://foo.test")); - request.headers.SetHeader(net::HttpRequestHeaders::kHost, "bar.test"); + request.url = GURL("https://example.com/path"); + request.request_initiator = url::Origin::Create(GURL("https://example.com")); + request.headers.SetHeader(net::HttpRequestHeaders::kHost, + "other.example.com"); CreateLoaderAndStart(request); RunUntilComplete(); @@ -1960,8 +1996,8 @@ TEST_F(CorsURLLoaderTest, RequestWithProxyAuthorizationHeaderFails) { request.mode = mojom::RequestMode::kCors; request.credentials_mode = mojom::CredentialsMode::kOmit; request.method = net::HttpRequestHeaders::kGetMethod; - request.url = GURL("https://foo.test/path"); - request.request_initiator = url::Origin::Create(GURL("https://foo.test")); + request.url = GURL("https://example.com/path"); + request.request_initiator = url::Origin::Create(GURL("https://example.com")); request.headers.SetHeader(net::HttpRequestHeaders::kProxyAuthorization, "Basic Zm9vOmJhcg=="); CreateLoaderAndStart(request); @@ -1979,8 +2015,8 @@ TEST_F(CorsURLLoaderTest, NoConcerningRequestHeadersLoggedCorrectly) { ResourceRequest request; request.mode = mojom::RequestMode::kNoCors; request.credentials_mode = mojom::CredentialsMode::kInclude; - request.url = GURL("http://example.com/"); - request.request_initiator = base::nullopt; + request.url = GURL("https://example.com/"); + request.request_initiator = url::Origin::Create(GURL("https://example.com")); request.headers.SetHeader("Not", "Concerning"); request.headers.SetHeader("Totally", "Fine"); @@ -2009,8 +2045,8 @@ TEST_F(CorsURLLoaderTest, ConcerningRequestHeadersLoggedCorrectly) { ResourceRequest request; request.mode = mojom::RequestMode::kNoCors; request.credentials_mode = mojom::CredentialsMode::kInclude; - request.url = GURL("http://example.com/"); - request.request_initiator = base::nullopt; + request.url = GURL("https://example.com/"); + request.request_initiator = url::Origin::Create(GURL("https://example.com")); request.headers.SetHeader(net::HttpRequestHeaders::kConnection, "Close"); request.headers.SetHeader(net::HttpRequestHeaders::kCookie, "BadIdea=true"); @@ -2043,7 +2079,8 @@ TEST_F(CorsURLLoaderTest, ConcerningRequestHeadersLoggedCorrectly) { } TEST_F(CorsURLLoaderTest, SetHostHeaderOnRedirectFails) { - CreateLoaderAndStart(GURL("http://foo.test/"), GURL("http://foo.test/path"), + CreateLoaderAndStart(GURL("https://example.com/"), + GURL("https://example.com/path"), mojom::RequestMode::kCors); RunUntilCreateLoaderAndStartCalled(); @@ -2071,7 +2108,8 @@ TEST_F(CorsURLLoaderTest, SetHostHeaderOnRedirectFails) { } TEST_F(CorsURLLoaderTest, SetProxyAuthorizationHeaderOnRedirectFails) { - CreateLoaderAndStart(GURL("http://foo.test/"), GURL("http://foo.test/path"), + CreateLoaderAndStart(GURL("https://example.com/"), + GURL("https://example.com/path"), mojom::RequestMode::kCors); RunUntilCreateLoaderAndStartCalled(); @@ -2100,10 +2138,17 @@ TEST_F(CorsURLLoaderTest, SetProxyAuthorizationHeaderOnRedirectFails) { } TEST_F(CorsURLLoaderTest, SameOriginCredentialsModeWithoutInitiator) { + // This test needs to simulate a factory used from the browser process, + // because only the browser process may start requests with no + // |request_initiator|. A renderer process would have run into NOTREACHED and + // mojo::ReportBadMessage via InitiatorLockCompatibility::kNoInitiator case in + // CorsURLLoaderFactory::IsValidRequest. + ResetFactory(base::nullopt /* initiator */, mojom::kBrowserProcessId); + ResourceRequest request; request.mode = mojom::RequestMode::kNoCors; request.credentials_mode = mojom::CredentialsMode::kSameOrigin; - request.url = GURL("http://example.com/"); + request.url = GURL("https://example.com/"); request.request_initiator = base::nullopt; BadMessageTestHelper bad_message_helper; @@ -2126,7 +2171,7 @@ TEST_F(CorsURLLoaderTest, SameOriginCredentialsModeOnNavigation) { ResourceRequest request; request.mode = mojom::RequestMode::kNavigate; request.credentials_mode = mojom::CredentialsMode::kSameOrigin; - request.url = GURL("http://example.com/"); + request.url = GURL("https://example.com/"); request.request_initiator = url::Origin::Create(request.url); BadMessageTestHelper bad_message_helper; @@ -2150,7 +2195,7 @@ TEST_F(CorsURLLoaderTest, OmitCredentialsModeOnNavigation) { ResourceRequest request; request.mode = mojom::RequestMode::kNavigate; request.credentials_mode = mojom::CredentialsMode::kOmit; - request.url = GURL("http://example.com/"); + request.url = GURL("https://example.com/"); request.request_initiator = url::Origin::Create(request.url); BadMessageTestHelper bad_message_helper; @@ -2171,11 +2216,13 @@ TEST_F(CorsURLLoaderTest, OmitCredentialsModeOnNavigation) { // Make sure than when a request is failed due to having |trusted_params| set // and being sent to an untrusted URLLoaderFactory, no CORS request is made. TEST_F(CorsURLLoaderTest, TrustedParamsWithUntrustedFactoryFailsBeforeCORS) { + url::Origin initiator = url::Origin::Create(GURL("https://example.com")); + // Run the test with a trusted URLLoaderFactory as well, to make sure a CORS // request is in fact made when using a trusted factory. for (bool is_trusted : {false, true}) { bool ignore_isolated_world_origin = true; // This is the default. - ResetFactory(base::nullopt, kRendererProcessId, is_trusted, + ResetFactory(initiator, kRendererProcessId, is_trusted, ignore_isolated_world_origin, false /* skip_cors_enabled_scheme_check */); @@ -2185,8 +2232,8 @@ TEST_F(CorsURLLoaderTest, TrustedParamsWithUntrustedFactoryFailsBeforeCORS) { request.mode = mojom::RequestMode::kCors; request.credentials_mode = mojom::CredentialsMode::kOmit; request.method = net::HttpRequestHeaders::kGetMethod; - request.url = GURL("http://other.com/foo.png"); - request.request_initiator = url::Origin::Create(GURL("http://example.com")); + request.url = GURL("http://other.example.com/foo.png"); + request.request_initiator = initiator; request.trusted_params = ResourceRequest::TrustedParams(); CreateLoaderAndStart(request); @@ -2205,7 +2252,7 @@ TEST_F(CorsURLLoaderTest, TrustedParamsWithUntrustedFactoryFailsBeforeCORS) { } else { RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnReceiveResponse( - {{"Access-Control-Allow-Origin", "http://example.com"}}); + {{"Access-Control-Allow-Origin", "https://example.com"}}); NotifyLoaderClientOnComplete(net::OK); RunUntilComplete(); @@ -2223,7 +2270,9 @@ TEST_F(CorsURLLoaderTest, TrustedParamsWithUntrustedFactoryFailsBeforeCORS) { // Test that when a request has LOAD_RESTRICTED_PREFETCH and a // NetworkIsolationKey, CorsURLLoaderFactory does not reject the request. TEST_F(CorsURLLoaderTest, RestrictedPrefetchSucceedsWithNIK) { - ResetFactory(base::nullopt, kRendererProcessId, true /* is_trusted */, + url::Origin initiator = url::Origin::Create(GURL("https://example.com")); + + ResetFactory(initiator, kRendererProcessId, true /* is_trusted */, true /* ignore_isolated_world_origin */, false /* skip_cors_enabled_scheme_check */); @@ -2233,8 +2282,8 @@ TEST_F(CorsURLLoaderTest, RestrictedPrefetchSucceedsWithNIK) { request.mode = mojom::RequestMode::kCors; request.credentials_mode = mojom::CredentialsMode::kOmit; request.method = net::HttpRequestHeaders::kGetMethod; - request.url = GURL("http://other.com/foo.png"); - request.request_initiator = url::Origin::Create(GURL("http://example.com")); + request.url = GURL("http://other.example.com/foo.png"); + request.request_initiator = initiator; request.load_flags |= net::LOAD_RESTRICTED_PREFETCH; request.trusted_params = ResourceRequest::TrustedParams(); @@ -2248,7 +2297,7 @@ TEST_F(CorsURLLoaderTest, RestrictedPrefetchSucceedsWithNIK) { RunUntilCreateLoaderAndStartCalled(); NotifyLoaderClientOnReceiveResponse( - {{"Access-Control-Allow-Origin", "http://example.com"}}); + {{"Access-Control-Allow-Origin", "https://example.com"}}); NotifyLoaderClientOnComplete(net::OK); RunUntilComplete(); @@ -2265,7 +2314,8 @@ TEST_F(CorsURLLoaderTest, RestrictedPrefetchSucceedsWithNIK) { // because the LOAD_RESTRICTED_PREFETCH flag must only appear on requests that // make use of their TrustedParams' |isolation_info|. TEST_F(CorsURLLoaderTest, RestrictedPrefetchFailsWithoutNIK) { - ResetFactory(base::nullopt, kRendererProcessId, true /* is_trusted */, + url::Origin initiator = url::Origin::Create(GURL("https://example.com")); + ResetFactory(initiator, kRendererProcessId, true /* is_trusted */, true /* ignore_isolated_world_origin */, false /* skip_cors_enabled_scheme_check */); @@ -2275,8 +2325,8 @@ TEST_F(CorsURLLoaderTest, RestrictedPrefetchFailsWithoutNIK) { request.mode = mojom::RequestMode::kCors; request.credentials_mode = mojom::CredentialsMode::kOmit; request.method = net::HttpRequestHeaders::kGetMethod; - request.url = GURL("http://other.com/foo.png"); - request.request_initiator = url::Origin::Create(GURL("http://example.com")); + request.url = GURL("http://other.example.com/foo.png"); + request.request_initiator = initiator; request.load_flags |= net::LOAD_RESTRICTED_PREFETCH; request.trusted_params = ResourceRequest::TrustedParams(); @@ -2464,70 +2514,6 @@ TEST_F(CorsURLLoaderTest, TAOCheckPassRedirect2) { EXPECT_TRUE(client().response_head()->timing_allow_passed); } -class CorsURLLoaderExtraSafelistedHeadersTest : public CorsURLLoaderTest { - public: - void SetUp() override { - auto params = mojom::NetworkContextParams::New(); - params->cors_extra_safelisted_request_header_names = { - "safelisted-1", "safelisted-2", "safelisted-3"}; - CorsURLLoaderTest::SetUp(std::move(params)); - } -}; - -TEST_F(CorsURLLoaderExtraSafelistedHeadersTest, ExtraSafelistedHeaders1) { - const GURL origin("https://example.com"); - const GURL url("https://other.example.com/foo.png"); - const GURL new_url("https://other2.example.com/bar.png"); - - ResourceRequest request; - request.mode = mojom::RequestMode::kCors; - request.credentials_mode = mojom::CredentialsMode::kOmit; - request.method = "GET"; - request.url = url; - request.request_initiator = url::Origin::Create(origin); - request.headers.SetHeader("safelisted-1", "foo"); - request.headers.SetHeader("safelisted-2", "bar"); - request.headers.SetHeader("safelisted-3", "baz"); - - CreateLoaderAndStart(request); - RunUntilCreateLoaderAndStartCalled(); - - // NO preflight request - ASSERT_EQ(1, num_created_loaders()); - EXPECT_EQ(GetRequest().url, url); - EXPECT_EQ(GetRequest().method, "GET"); -} - -TEST_F(CorsURLLoaderExtraSafelistedHeadersTest, ExtraSafelistedHeaders2) { - const GURL origin("https://example.com"); - const GURL url("https://other.example.com/foo.png"); - const GURL new_url("https://other2.example.com/bar.png"); - - ResourceRequest request; - request.mode = mojom::RequestMode::kCors; - request.credentials_mode = mojom::CredentialsMode::kOmit; - request.method = "GET"; - request.url = url; - request.request_initiator = url::Origin::Create(origin); - request.headers.SetHeader("safelisted-1", "foo"); - request.headers.SetHeader("safelisted-2", "bar"); - request.headers.SetHeader("hoge", "fuga"); - request.headers.SetHeader("piyo", "hogera"); - - CreateLoaderAndStart(request); - RunUntilCreateLoaderAndStartCalled(); - - // preflight request - ASSERT_EQ(1, num_created_loaders()); - EXPECT_EQ(GetRequest().url, url); - EXPECT_EQ(GetRequest().method, "OPTIONS"); - - std::string headers; - EXPECT_TRUE(GetRequest().headers.GetHeader("access-control-request-headers", - &headers)); - EXPECT_EQ(headers, "hoge,piyo"); -} - } // namespace } // namespace cors diff --git a/chromium/services/network/cors/preflight_controller.cc b/chromium/services/network/cors/preflight_controller.cc index 84bc1ec35d0..11e1a9fad64 100644 --- a/chromium/services/network/cors/preflight_controller.cc +++ b/chromium/services/network/cors/preflight_controller.cc @@ -55,15 +55,13 @@ base::Optional<std::string> GetHeaderString( // - byte-lowercased std::string CreateAccessControlRequestHeadersHeader( const net::HttpRequestHeaders& headers, - bool is_revalidating, - const base::flat_set<std::string>& extra_safelisted_header_names) { + bool is_revalidating) { // Exclude the forbidden headers because they may be added by the user // agent. They must be checked separately and rejected for // JavaScript-initiated requests. std::vector<std::string> filtered_headers = CorsUnsafeNotForbiddenRequestHeaderNames(headers.GetHeaderVector(), - is_revalidating, - extra_safelisted_header_names); + is_revalidating); if (filtered_headers.empty()) return std::string(); @@ -76,7 +74,6 @@ std::string CreateAccessControlRequestHeadersHeader( std::unique_ptr<ResourceRequest> CreatePreflightRequest( const ResourceRequest& request, bool tainted, - const base::flat_set<std::string>& extra_safelisted_header_names, const base::Optional<base::UnguessableToken>& devtools_request_id) { DCHECK(!request.url.has_username()); DCHECK(!request.url.has_password()); @@ -92,6 +89,7 @@ std::unique_ptr<ResourceRequest> CreatePreflightRequest( preflight_request->destination = request.destination; preflight_request->referrer = request.referrer; preflight_request->referrer_policy = request.referrer_policy; + preflight_request->mode = mojom::RequestMode::kCors; preflight_request->credentials_mode = mojom::CredentialsMode::kOmit; preflight_request->load_flags = RetrieveCacheFlags(request.load_flags); @@ -106,7 +104,7 @@ std::unique_ptr<ResourceRequest> CreatePreflightRequest( header_names::kAccessControlRequestMethod, request.method); std::string request_headers = CreateAccessControlRequestHeadersHeader( - request.headers, request.is_revalidating, extra_safelisted_header_names); + request.headers, request.is_revalidating); if (!request_headers.empty()) { preflight_request->headers.SetHeader( header_names::kAccessControlRequestHeaders, request_headers); @@ -123,6 +121,17 @@ std::unique_ptr<ResourceRequest> CreatePreflightRequest( net::HttpRequestHeaders::kOrigin, (tainted ? url::Origin() : *request.request_initiator).Serialize()); + // We normally set User-Agent down in the network stack, but the DevTools + // emulation override is applied on a higher level (renderer or browser), + // so copy User-Agent from the original request, if present. + // TODO(caseq, morlovich): do the same for client hints. + std::string user_agent; + if (request.headers.GetHeader(net::HttpRequestHeaders::kUserAgent, + &user_agent)) { + preflight_request->headers.SetHeader(net::HttpRequestHeaders::kUserAgent, + user_agent); + } + // Additional headers that the algorithm in the spec does not require, but // it's better that CORS preflight requests have them. preflight_request->headers.SetHeader("Sec-Fetch-Mode", "cors"); @@ -178,16 +187,14 @@ std::unique_ptr<PreflightResult> CreatePreflightResult( base::Optional<CorsErrorStatus> CheckPreflightResult( PreflightResult* result, - const ResourceRequest& original_request, - const base::flat_set<std::string>& extra_safelisted_header_names) { + const ResourceRequest& original_request) { base::Optional<CorsErrorStatus> status = result->EnsureAllowedCrossOriginMethod(original_request.method); if (status) return status; return result->EnsureAllowedCrossOriginHeaders( - original_request.headers, original_request.is_revalidating, - extra_safelisted_header_names); + original_request.headers, original_request.is_revalidating); } } // namespace @@ -211,9 +218,8 @@ class PreflightController::PreflightLoader final { auto* network_service_client = MaybeGetNetworkServiceClientForDevTools(); if (network_service_client) devtools_request_id_ = base::UnguessableToken::Create(); - auto preflight_request = CreatePreflightRequest( - request, tainted, controller->extra_safelisted_header_names(), - devtools_request_id_); + auto preflight_request = + CreatePreflightRequest(request, tainted, devtools_request_id_); if (network_service_client) { DCHECK(devtools_request_id_); @@ -291,8 +297,7 @@ class PreflightController::PreflightLoader final { // Preflight succeeded. Check |original_request_| with |result|. DCHECK(!detected_error_status); detected_error_status = - CheckPreflightResult(result.get(), original_request_, - controller_->extra_safelisted_header_names()); + CheckPreflightResult(result.get(), original_request_); } if (!(original_request_.load_flags & net::LOAD_DISABLE_CACHE) && @@ -370,7 +375,7 @@ std::unique_ptr<ResourceRequest> PreflightController::CreatePreflightRequestForTesting( const ResourceRequest& request, bool tainted) { - return CreatePreflightRequest(request, tainted, {}, base::nullopt); + return CreatePreflightRequest(request, tainted, base::nullopt); } // static @@ -385,12 +390,8 @@ PreflightController::CreatePreflightResultForTesting( detected_error_status); } -PreflightController::PreflightController( - const std::vector<std::string>& extra_safelisted_header_names, - NetworkService* network_service) - : extra_safelisted_header_names_(extra_safelisted_header_names.cbegin(), - extra_safelisted_header_names.cend()), - network_service_(network_service) {} +PreflightController::PreflightController(NetworkService* network_service) + : network_service_(network_service) {} PreflightController::~PreflightController() = default; diff --git a/chromium/services/network/cors/preflight_controller.h b/chromium/services/network/cors/preflight_controller.h index 437840d7049..7e7cf4bec8b 100644 --- a/chromium/services/network/cors/preflight_controller.h +++ b/chromium/services/network/cors/preflight_controller.h @@ -52,9 +52,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) PreflightController final { bool tainted, base::Optional<CorsErrorStatus>* detected_error_status); - PreflightController( - const std::vector<std::string>& extra_safelisted_header_names, - NetworkService* network_service); + explicit PreflightController(NetworkService* network_service); ~PreflightController(); // Determines if a CORS-preflight request is needed, and checks the cache, or @@ -70,15 +68,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) PreflightController final { int32_t process_id, const net::IsolationInfo& isolation_info); - const base::flat_set<std::string>& extra_safelisted_header_names() const { - return extra_safelisted_header_names_; - } - - void set_extra_safelisted_header_names( - const base::flat_set<std::string>& extra_safelisted_header_names) { - extra_safelisted_header_names_ = extra_safelisted_header_names; - } - private: class PreflightLoader; @@ -94,8 +83,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) PreflightController final { std::set<std::unique_ptr<PreflightLoader>, base::UniquePtrComparator> loaders_; - base::flat_set<std::string> extra_safelisted_header_names_; - NetworkService* const network_service_; DISALLOW_COPY_AND_ASSIGN(PreflightController); diff --git a/chromium/services/network/cors/preflight_controller_unittest.cc b/chromium/services/network/cors/preflight_controller_unittest.cc index c252df4121a..effbbabe67e 100644 --- a/chromium/services/network/cors/preflight_controller_unittest.cc +++ b/chromium/services/network/cors/preflight_controller_unittest.cc @@ -229,8 +229,7 @@ TEST(PreflightControllerOptionsTest, CheckOptions) { base::test::TaskEnvironment task_environment_( base::test::TaskEnvironment::MainThreadType::IO); TestURLLoaderFactory url_loader_factory; - PreflightController preflight_controller( - {} /* extra_safelisted_header_names */, nullptr /* network_service */); + PreflightController preflight_controller(nullptr /* network_service */); network::ResourceRequest request; request.url = GURL("https://example.com/"); @@ -300,7 +299,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { int32_t process_id, int32_t routing_id, const std::string& devtools_request_id, - const net::CookieAndLineStatusList& cookies_with_status, + const net::CookieAndLineAccessResultList& cookies_with_access_result, std::vector<network::mojom::HttpRawHeaderPairPtr> headers, const base::Optional<std::string>& raw_response_headers) override { on_raw_response_called_ = true; @@ -364,6 +363,10 @@ class PreflightControllerTest : public testing::Test { network::mojom::URLLoaderFactoryParamsPtr params = network::mojom::URLLoaderFactoryParams::New(); params->process_id = mojom::kBrowserProcessId; + // We use network::CorsURLLoaderFactory for "internal" URLLoaderFactory + // used by the PreflightController. Hence here we disable CORS as otherwise + // the URLLoader would create a CORS-preflight for the preflight request. + params->disable_web_security = true; params->is_corb_enabled = false; network_context_remote_->CreateURLLoaderFactory( url_loader_factory_remote_.BindNewPipeAndPassReceiver(), @@ -418,8 +421,8 @@ class PreflightControllerTest : public testing::Test { void SetUp() override { SetAccessControlAllowOrigin(test_initiator_origin_); - preflight_controller_ = std::make_unique<PreflightController>( - std::vector<std::string>(), network_service_.get()); + preflight_controller_ = + std::make_unique<PreflightController>(network_service_.get()); test_server_.RegisterRequestHandler(base::BindRepeating( &PreflightControllerTest::ServePreflight, base::Unretained(this))); diff --git a/chromium/services/network/crash_keys.cc b/chromium/services/network/crash_keys.cc index 34e493d07ef..c4ddd387760 100644 --- a/chromium/services/network/crash_keys.cc +++ b/chromium/services/network/crash_keys.cc @@ -27,9 +27,9 @@ base::debug::CrashKeyString* GetRequestInitiatorCrashKey() { } // namespace -base::debug::CrashKeyString* GetRequestInitiatorSiteLockCrashKey() { +base::debug::CrashKeyString* GetRequestInitiatorOriginLockCrashKey() { static auto* crash_key = base::debug::AllocateCrashKeyString( - "request_initiator_site_lock", base::debug::CrashKeySize::Size64); + "request_initiator_origin_lock", base::debug::CrashKeySize::Size64); return crash_key; } diff --git a/chromium/services/network/crash_keys.h b/chromium/services/network/crash_keys.h index cab44590e6e..52e96115827 100644 --- a/chromium/services/network/crash_keys.h +++ b/chromium/services/network/crash_keys.h @@ -15,7 +15,7 @@ struct ResourceRequest; namespace debug { -base::debug::CrashKeyString* GetRequestInitiatorSiteLockCrashKey(); +base::debug::CrashKeyString* GetRequestInitiatorOriginLockCrashKey(); class ScopedRequestCrashKeys { public: diff --git a/chromium/services/network/cross_origin_read_blocking_exception_for_plugin.cc b/chromium/services/network/cross_origin_read_blocking_exception_for_plugin.cc new file mode 100644 index 00000000000..53faac40eae --- /dev/null +++ b/chromium/services/network/cross_origin_read_blocking_exception_for_plugin.cc @@ -0,0 +1,43 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <set> + +#include "base/no_destructor.h" +#include "base/stl_util.h" +#include "services/network/cross_origin_read_blocking_exception_for_plugin.h" + +namespace network { + +namespace { + +std::set<int>& GetPluginProxyingProcesses() { + static base::NoDestructor<std::set<int>> set; + return *set; +} + +} // namespace + +// static +void CrossOriginReadBlockingExceptionForPlugin::AddExceptionForPlugin( + int process_id) { + std::set<int>& plugin_proxies = GetPluginProxyingProcesses(); + plugin_proxies.insert(process_id); +} + +// static +bool CrossOriginReadBlockingExceptionForPlugin::ShouldAllowForPlugin( + int process_id) { + std::set<int>& plugin_proxies = GetPluginProxyingProcesses(); + return base::Contains(plugin_proxies, process_id); +} + +// static +void CrossOriginReadBlockingExceptionForPlugin::RemoveExceptionForPlugin( + int process_id) { + std::set<int>& plugin_proxies = GetPluginProxyingProcesses(); + plugin_proxies.erase(process_id); +} + +} // namespace network diff --git a/chromium/services/network/cross_origin_read_blocking_exception_for_plugin.h b/chromium/services/network/cross_origin_read_blocking_exception_for_plugin.h new file mode 100644 index 00000000000..820347506ea --- /dev/null +++ b/chromium/services/network/cross_origin_read_blocking_exception_for_plugin.h @@ -0,0 +1,38 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_NETWORK_CROSS_ORIGIN_READ_BLOCKING_EXCEPTION_FOR_PLUGIN_H_ +#define SERVICES_NETWORK_CROSS_ORIGIN_READ_BLOCKING_EXCEPTION_FOR_PLUGIN_H_ + +#include "base/component_export.h" +#include "base/macros.h" + +namespace network { + +class COMPONENT_EXPORT(NETWORK_SERVICE) + CrossOriginReadBlockingExceptionForPlugin { + public: + // Notifies CORB that |process_id| is proxying requests on behalf of a + // universal-access plugin and therefore CORB should stop blocking requests + // marked as ResourceType::kPluginResource. + // + // TODO(lukasza, laforge): https://crbug.com/702995: Remove the static + // ...ForPlugin methods once Flash support is removed from Chromium (probably + // around 2020 - see https://www.chromium.org/flash-roadmap). + static void AddExceptionForPlugin(int process_id); + + // Returns true if CORB should ignore a request initiated by a universal + // access plugin - i.e. if |process_id| has been previously passed to + // AddExceptionForPlugin. + static bool ShouldAllowForPlugin(int process_id); + + // Reverts AddExceptionForPlugin. + static void RemoveExceptionForPlugin(int process_id); + + DISALLOW_COPY_AND_ASSIGN(CrossOriginReadBlockingExceptionForPlugin); +}; + +} // namespace network + +#endif // SERVICES_NETWORK_CROSS_ORIGIN_READ_BLOCKING_EXCEPTION_FOR_PLUGIN_H_ diff --git a/chromium/services/network/cross_origin_read_blocking_explainer.md b/chromium/services/network/cross_origin_read_blocking_explainer.md index fe55d43dbd2..f29500ac6db 100644 --- a/chromium/services/network/cross_origin_read_blocking_explainer.md +++ b/chromium/services/network/cross_origin_read_blocking_explainer.md @@ -680,8 +680,8 @@ which are distributed via JSON (which is CORB-protected). In the future CORB may be extended to protect additional resources as follows: * **Covering more MIME types**. - Instead of blacklisting HTML, XML, and JSON, CORB protection can be extended to - all MIME types, except MIME types that are whitelisted as usable in `<img>`, + Instead of blocklisting HTML, XML, and JSON, CORB protection can be extended to + all MIME types, except MIME types that are allowlisted as usable in `<img>`, `<audio>`, `<video>`, `<script>` and other similar elements that can be embedded cross-origin: * [JavaScript MIME type](https://html.spec.whatwg.org/#javascript-mime-type) diff --git a/chromium/services/network/expect_ct_reporter.h b/chromium/services/network/expect_ct_reporter.h index 559229601f2..30ea3b69656 100644 --- a/chromium/services/network/expect_ct_reporter.h +++ b/chromium/services/network/expect_ct_reporter.h @@ -26,7 +26,7 @@ namespace network { // the URLRequestContext that is passed to the constructor, so that it // can cancel its requests. // -// Since reports are sent with a non-CORS-whitelisted Content-Type, this class +// Since reports are sent with a non-CORS-allowlisted Content-Type, this class // sends CORS preflight requests before sending reports. Expect-CT is not // evaluated with a particular frame or request as context, so the preflight // request contains an `Origin: null` header instead of a particular origin. diff --git a/chromium/services/network/expect_ct_reporter_unittest.cc b/chromium/services/network/expect_ct_reporter_unittest.cc index 08449291e7c..e2fc979fdc7 100644 --- a/chromium/services/network/expect_ct_reporter_unittest.cc +++ b/chromium/services/network/expect_ct_reporter_unittest.cc @@ -48,12 +48,12 @@ class TestCertificateReportSender : public net::ReportSender { : ReportSender(nullptr, TRAFFIC_ANNOTATION_FOR_TESTS) {} ~TestCertificateReportSender() override {} - void Send(const GURL& report_uri, - base::StringPiece content_type, - base::StringPiece serialized_report, - const base::RepeatingCallback<void()>& success_callback, - const base::RepeatingCallback<void(const GURL&, int, int)>& - error_callback) override { + void Send( + const GURL& report_uri, + base::StringPiece content_type, + base::StringPiece serialized_report, + base::OnceCallback<void()> success_callback, + base::OnceCallback<void(const GURL&, int, int)> error_callback) override { sent_report_count_++; latest_report_uri_ = report_uri; latest_serialized_report_.assign(serialized_report.data(), diff --git a/chromium/services/network/host_resolver.cc b/chromium/services/network/host_resolver.cc index 561a1c22a90..1711176ecea 100644 --- a/chromium/services/network/host_resolver.cc +++ b/chromium/services/network/host_resolver.cc @@ -15,6 +15,7 @@ #include "net/dns/host_resolver.h" #include "net/dns/host_resolver_source.h" #include "net/log/net_log.h" +#include "net/net_buildflags.h" #include "services/network/host_resolver_mdns_listener.h" #include "services/network/public/cpp/host_resolver_mojom_traits.h" #include "services/network/resolve_host_request.h" diff --git a/chromium/services/network/host_resolver_unittest.cc b/chromium/services/network/host_resolver_unittest.cc index 509ff744f55..a7ba3ed9f2e 100644 --- a/chromium/services/network/host_resolver_unittest.cc +++ b/chromium/services/network/host_resolver_unittest.cc @@ -31,6 +31,7 @@ #include "net/dns/mock_host_resolver.h" #include "net/dns/public/dns_protocol.h" #include "net/log/net_log.h" +#include "net/net_buildflags.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/services/network/ignore_errors_cert_verifier.h b/chromium/services/network/ignore_errors_cert_verifier.h index a2ab2338d19..0c7f5324cdb 100644 --- a/chromium/services/network/ignore_errors_cert_verifier.h +++ b/chromium/services/network/ignore_errors_cert_verifier.h @@ -57,7 +57,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) IgnoreErrorsCertVerifier private: friend class IgnoreErrorsCertVerifierTest; - void SetAllowlistForTesting(const SPKIHashSet& whitelist); + void SetAllowlistForTesting(const SPKIHashSet& allowlist); std::unique_ptr<net::CertVerifier> verifier_; SPKIHashSet allowlist_; diff --git a/chromium/services/network/net_log_exporter.cc b/chromium/services/network/net_log_exporter.cc index 717076e1fe1..d5694bec240 100644 --- a/chromium/services/network/net_log_exporter.cc +++ b/chromium/services/network/net_log_exporter.cc @@ -90,13 +90,13 @@ void NetLogExporter::Stop(base::Value polled_data_value, return; } - std::unique_ptr<base::DictionaryValue> net_info = net::GetNetInfo( + base::Value net_info = net::GetNetInfo( network_context_->url_request_context(), net::NET_INFO_ALL_SOURCES); if (polled_data) - net_info->MergeDictionary(polled_data); + net_info.MergeDictionary(polled_data); file_net_observer_->StopObserving( - std::move(net_info), + base::Value::ToUniquePtrValue(std::move(net_info)), base::BindOnce([](StopCallback sc) { std::move(sc).Run(net::OK); }, std::move(callback))); file_net_observer_ = nullptr; @@ -150,14 +150,9 @@ void NetLogExporter::StartWithScratchDirOrCleanup( base::ThreadPool::PostTask( FROM_HERE, {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}, - base::BindOnce( - [](const base::FilePath& dir) { - // The delete is non-recursive (2nd argument - // false) since the only time this is invoked - // the directory is expected to be empty. - base::DeleteFile(dir, false); - }, - scratch_dir_path)); + // The delete is non-recursive since the only time this is invoked is + // when the directory is expected to be empty. + base::BindOnce(base::GetDeleteFileCallback(), scratch_dir_path)); } } diff --git a/chromium/services/network/network_context.cc b/chromium/services/network/network_context.cc index 3bb208bad23..5b4588fadf4 100644 --- a/chromium/services/network/network_context.cc +++ b/chromium/services/network/network_context.cc @@ -5,6 +5,7 @@ #include "services/network/network_context.h" #include <memory> +#include <string> #include <utility> #include "base/barrier_closure.h" @@ -17,13 +18,13 @@ #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop_current.h" #include "base/metrics/histogram_functions.h" #include "base/optional.h" #include "base/sequenced_task_runner.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/current_thread.h" #include "base/task/post_task.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" @@ -322,6 +323,52 @@ std::string HashesToBase64String(const net::HashValueVector& hashes) { return str; } +#if BUILDFLAG(IS_CT_SUPPORTED) +// SCTAuditingDelegate is an implementation of the delegate interface that is +// aware of per-NetworkContext details (to allow the cache to notify the +// associated NetworkContextClient of new reports, and to apply +// per-NetworkContext enabled/disabled status for the auditing feature). +class SCTAuditingDelegate : public net::SCTAuditingDelegate { + public: + explicit SCTAuditingDelegate(const base::WeakPtr<NetworkContext>& context); + ~SCTAuditingDelegate() override; + + // net::SCTAuditingDelegate: + void MaybeEnqueueReport( + const net::HostPortPair& host_port_pair, + const net::X509Certificate* validated_certificate_chain, + const net::SignedCertificateTimestampAndStatusList& + signed_certificate_timestamps) override; + bool IsSCTAuditingEnabled() override; + + private: + base::WeakPtr<NetworkContext> context_; +}; + +SCTAuditingDelegate::SCTAuditingDelegate( + const base::WeakPtr<NetworkContext>& context) + : context_(context) {} + +SCTAuditingDelegate::~SCTAuditingDelegate() = default; + +void SCTAuditingDelegate::MaybeEnqueueReport( + const net::HostPortPair& host_port_pair, + const net::X509Certificate* validated_certificate_chain, + const net::SignedCertificateTimestampAndStatusList& + signed_certificate_timestamps) { + if (!context_) + return; + context_->MaybeEnqueueSCTReport(host_port_pair, validated_certificate_chain, + signed_certificate_timestamps); +} + +bool SCTAuditingDelegate::IsSCTAuditingEnabled() { + if (!context_) + return false; + return context_->is_sct_auditing_enabled(); +} +#endif // BUILDFLAG(IS_CT_SUPPORTED) + } // namespace constexpr uint32_t NetworkContext::kMaxOutstandingRequestsPerProcess; @@ -344,9 +391,7 @@ NetworkContext::NetworkContext( std::make_unique<NetworkContextApplicationStatusListener>()), #endif receiver_(this, std::move(receiver)), - cors_preflight_controller_( - params_->cors_extra_safelisted_request_header_names, - network_service) { + cors_preflight_controller_(network_service) { mojo::PendingRemote<mojom::URLLoaderFactory> url_loader_factory_for_cert_net_fetcher; mojo::PendingReceiver<mojom::URLLoaderFactory> @@ -417,7 +462,7 @@ NetworkContext::NetworkContext( socket_factory_( std::make_unique<SocketFactory>(url_request_context_->net_log(), url_request_context)), - cors_preflight_controller_(std::vector<std::string>(), network_service) { + cors_preflight_controller_(network_service) { // May be nullptr in tests. if (network_service_) network_service_->RegisterNetworkContext(this); @@ -1106,6 +1151,20 @@ void NetworkContext::GetExpectCTState( std::move(callback).Run(std::move(result)); } + +void NetworkContext::MaybeEnqueueSCTReport( + const net::HostPortPair& host_port_pair, + const net::X509Certificate* validated_certificate_chain, + const net::SignedCertificateTimestampAndStatusList& + signed_certificate_timestamps) { + network_service()->sct_auditing_cache()->MaybeEnqueueReport( + this, host_port_pair, validated_certificate_chain, + signed_certificate_timestamps); +} + +void NetworkContext::SetSCTAuditingEnabled(bool enabled) { + is_sct_auditing_enabled_ = enabled; +} #endif // BUILDFLAG(IS_CT_SUPPORTED) void NetworkContext::CreateUDPSocket( @@ -1204,16 +1263,25 @@ void NetworkContext::CreateWebSocket( int32_t render_frame_id, const url::Origin& origin, uint32_t options, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, mojo::PendingRemote<mojom::WebSocketHandshakeClient> handshake_client, mojo::PendingRemote<mojom::AuthenticationHandler> auth_handler, mojo::PendingRemote<mojom::TrustedHeaderClient> header_client) { #if !defined(OS_IOS) if (!websocket_factory_) websocket_factory_ = std::make_unique<WebSocketFactory>(this); + + DCHECK_GE(process_id, 0); + if (process_id == mojom::kBrowserProcessId) { + DCHECK_EQ(render_frame_id, 0); + } + websocket_factory_->CreateWebSocket( url, requested_protocols, site_for_cookies, isolation_info, std::move(additional_headers), process_id, render_frame_id, origin, - options, std::move(handshake_client), std::move(auth_handler), + options, + static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation), + std::move(handshake_client), std::move(auth_handler), std::move(header_client)); #endif // !defined(OS_IOS) } @@ -1348,15 +1416,6 @@ void NetworkContext::SetCorsOriginAccessListsForOrigin( std::move(callback).Run(); } -void NetworkContext::SetCorsExtraSafelistedRequestHeaderNames( - const std::vector<std::string>& - cors_extra_safelisted_request_header_names) { - cors_preflight_controller_.set_extra_safelisted_header_names( - base::flat_set<std::string>( - cors_extra_safelisted_request_header_names.cbegin(), - cors_extra_safelisted_request_header_names.cend())); -} - void NetworkContext::AddHSTS(const std::string& host, base::Time expiry, bool include_subdomains, @@ -1667,6 +1726,44 @@ void NetworkContext::LookupServerBasicAuthCredentials( std::move(callback).Run(base::nullopt); } +#if defined(OS_CHROMEOS) +void NetworkContext::LookupProxyAuthCredentials( + const net::ProxyServer& proxy_server, + const std::string& auth_scheme, + const std::string& realm, + LookupProxyAuthCredentialsCallback callback) { + net::HttpAuth::Scheme net_scheme = + net::HttpAuth::StringToScheme(base::ToLowerASCII(auth_scheme)); + if (net_scheme == net::HttpAuth::Scheme::AUTH_SCHEME_MAX) { + std::move(callback).Run(base::nullopt); + return; + } + net::HttpAuthCache* http_auth_cache = + url_request_context_->http_transaction_factory() + ->GetSession() + ->http_auth_cache(); + // TODO(https://crbug.com/1103768): Mapping proxy addresses to URLs is a + // lossy conversion, shouldn't do this. + const char* scheme = + proxy_server.is_secure_http_like() ? "https://" : "http://"; + GURL proxy_url(scheme + proxy_server.host_port_pair().ToString()); + if (!proxy_url.is_valid()) { + std::move(callback).Run(base::nullopt); + return; + } + + // Unlike server credentials, proxy credentials are not keyed on + // NetworkIsolationKey. + net::HttpAuthCache::Entry* entry = + http_auth_cache->Lookup(proxy_url, net::HttpAuth::AUTH_PROXY, realm, + net_scheme, net::NetworkIsolationKey()); + if (entry) + std::move(callback).Run(entry->credentials()); + else + std::move(callback).Run(base::nullopt); +} +#endif + const net::HttpAuthPreferences* NetworkContext::GetHttpAuthPreferences() const { return &http_auth_merged_preferences_; } @@ -1971,6 +2068,8 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( if (params_->reporting_delivery_interval) { reporting_policy->delivery_interval = *params_->reporting_delivery_interval; + reporting_policy->endpoint_backoff_policy.initial_delay_ms = + params_->reporting_delivery_interval->InMilliseconds(); } builder.set_reporting_policy(std::move(reporting_policy)); } else { @@ -2071,6 +2170,9 @@ URLRequestContextOwner NetworkContext::MakeURLRequestContext( params_->ct_log_update_time, disqualified_logs, operated_by_google_logs)); } + + builder.set_sct_auditing_delegate( + std::make_unique<SCTAuditingDelegate>(weak_factory_.GetWeakPtr())); #endif // BUILDFLAG(IS_CT_SUPPORTED) builder.set_host_mapping_rules( @@ -2299,6 +2401,13 @@ void NetworkContext::OnCertVerifyForSignedExchangeComplete(int cert_verify_id, ct_verify_result.policy_compliance, net::NetworkIsolationKey::Todo()); + if (url_request_context_->sct_auditing_delegate() && + url_request_context_->sct_auditing_delegate()->IsSCTAuditingEnabled()) { + url_request_context_->sct_auditing_delegate()->MaybeEnqueueReport( + net::HostPortPair::FromURL(pending_cert_verify->url), verified_cert, + ct_verify_result.scts); + } + switch (ct_requirement_status) { case net::TransportSecurityState::CT_REQUIREMENTS_NOT_MET: result = net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED; diff --git a/chromium/services/network/network_context.h b/chromium/services/network/network_context.h index eced38ff04f..2025d29440b 100644 --- a/chromium/services/network/network_context.h +++ b/chromium/services/network/network_context.h @@ -54,6 +54,7 @@ #include "services/network/public/mojom/udp_socket.mojom.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "services/network/public/mojom/websocket.mojom.h" +#include "services/network/sct_auditing_cache.h" #include "services/network/socket_factory.h" #include "services/network/url_request_context_owner.h" @@ -258,6 +259,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext void GetExpectCTState(const std::string& domain, const net::NetworkIsolationKey& network_isolation_key, GetExpectCTStateCallback callback) override; + void MaybeEnqueueSCTReport( + const net::HostPortPair& host_port_pair, + const net::X509Certificate* validated_certificate_chain, + const net::SignedCertificateTimestampAndStatusList& + signed_certificate_timestamps); + void SetSCTAuditingEnabled(bool enabled) override; + bool is_sct_auditing_enabled() { return is_sct_auditing_enabled_; } #endif // BUILDFLAG(IS_CT_SUPPORTED) void CreateUDPSocket( mojo::PendingReceiver<mojom::UDPSocket> receiver, @@ -300,6 +308,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext int32_t render_frame_id, const url::Origin& origin, uint32_t options, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation, mojo::PendingRemote<mojom::WebSocketHandshakeClient> handshake_client, mojo::PendingRemote<mojom::AuthenticationHandler> auth_handler, mojo::PendingRemote<mojom::TrustedHeaderClient> header_client) override; @@ -345,9 +354,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext std::vector<mojom::CorsOriginPatternPtr> allow_patterns, std::vector<mojom::CorsOriginPatternPtr> block_patterns, SetCorsOriginAccessListsForOriginCallback callback) override; - void SetCorsExtraSafelistedRequestHeaderNames( - const std::vector<std::string>& - cors_extra_safelisted_request_header_names) override; void EnableStaticKeyPinningForTesting( EnableStaticKeyPinningForTestingCallback callback) override; void VerifyCertificateForTesting( @@ -400,6 +406,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext const GURL& url, const net::NetworkIsolationKey& network_isolation_key, LookupServerBasicAuthCredentialsCallback callback) override; +#if defined(OS_CHROMEOS) + void LookupProxyAuthCredentials( + const net::ProxyServer& proxy_server, + const std::string& auth_scheme, + const std::string& realm, + LookupProxyAuthCredentialsCallback callback) override; +#endif void GetOriginPolicyManager( mojo::PendingReceiver<mojom::OriginPolicyManager> receiver) override; @@ -494,6 +507,12 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext return trust_token_store_.get(); } +#if BUILDFLAG(IS_CT_SUPPORTED) + void SetIsSCTAuditingEnabledForTesting(bool enabled) { + is_sct_auditing_enabled_ = enabled; + } +#endif // BUILDFLAG(IS_CT_SUPPORTED) + private: URLRequestContextOwner MakeURLRequestContext( mojo::PendingRemote<mojom::URLLoaderFactory> @@ -647,6 +666,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext std::queue<SetExpectCTTestReportCallback> outstanding_set_expect_ct_callbacks_; + + bool is_sct_auditing_enabled_ = false; #endif // BUILDFLAG(IS_CT_SUPPORTED) #if defined(OS_CHROMEOS) diff --git a/chromium/services/network/network_context_unittest.cc b/chromium/services/network/network_context_unittest.cc index 22955e89804..dc8f31a7766 100644 --- a/chromium/services/network/network_context_unittest.cc +++ b/chromium/services/network/network_context_unittest.cc @@ -67,7 +67,7 @@ #include "net/cert/cert_verify_result.h" #include "net/cert/mock_cert_verifier.h" #include "net/cookies/canonical_cookie.h" -#include "net/cookies/cookie_inclusion_status.h" +#include "net/cookies/cookie_access_result.h" #include "net/cookies/cookie_options.h" #include "net/cookies/cookie_store.h" #include "net/cookies/cookie_util.h" @@ -103,6 +103,7 @@ #include "net/test/spawned_test_server/spawned_test_server.h" #include "net/test/test_data_directory.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +#include "net/url_request/referrer_policy.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" #include "net/url_request/url_request_job_factory.h" @@ -122,11 +123,11 @@ #include "services/network/public/mojom/url_loader.mojom-shared.h" #include "services/network/test/fake_test_cert_verifier_params_factory.h" #include "services/network/test/test_url_loader_client.h" +#include "services/network/test/udp_socket_test_util.h" +#include "services/network/test_mojo_proxy_resolver_factory.h" #include "services/network/trust_tokens/pending_trust_token_store.h" #include "services/network/trust_tokens/trust_token_parameterization.h" #include "services/network/trust_tokens/trust_token_store.h" -#include "services/network/udp_socket_test_util.h" -#include "test_mojo_proxy_resolver_factory.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -1364,9 +1365,8 @@ TEST_F(NetworkContextTest, Referrers) { ASSERT_TRUE(test_server.Start()); for (bool validate_referrer_policy_on_initial_request : {false, true}) { - for (net::URLRequest::ReferrerPolicy referrer_policy : - {net::URLRequest::NEVER_CLEAR_REFERRER, - net::URLRequest::NO_REFERRER}) { + for (net::ReferrerPolicy referrer_policy : + {net::ReferrerPolicy::NEVER_CLEAR, net::ReferrerPolicy::NO_REFERRER}) { mojom::NetworkContextParamsPtr context_params = CreateContextParams(); context_params->validate_referrer_policy_on_initial_request = validate_referrer_policy_on_initial_request; @@ -1399,7 +1399,7 @@ TEST_F(NetworkContextTest, Referrers) { // If validating referrers, and the referrer policy is not to send // referrers, the request should fail. if (validate_referrer_policy_on_initial_request && - referrer_policy == net::URLRequest::NO_REFERRER) { + referrer_policy == net::ReferrerPolicy::NO_REFERRER) { EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, client.completion_status().error_code); EXPECT_FALSE(client.response_body().is_valid()); @@ -1412,7 +1412,7 @@ TEST_F(NetworkContextTest, Referrers) { ASSERT_TRUE(client.response_body().is_valid()); EXPECT_TRUE(mojo::BlockingCopyToString(client.response_body_release(), &response_body)); - if (referrer_policy == net::URLRequest::NO_REFERRER) { + if (referrer_policy == net::ReferrerPolicy::NO_REFERRER) { // If not validating referrers, and the referrer policy is not to send // referrers, the referrer should be cleared. EXPECT_EQ("None", response_body); @@ -1905,6 +1905,116 @@ TEST_F(NetworkContextTest, LookupServerBasicAuthCredentials) { EXPECT_FALSE(result.has_value()); } +#if defined(OS_CHROMEOS) +base::Optional<net::AuthCredentials> GetProxyAuthCredentials( + NetworkContext* network_context, + const net::ProxyServer& proxy_server, + const std::string& scheme, + const std::string& realm) { + base::RunLoop run_loop; + base::Optional<net::AuthCredentials> result; + network_context->LookupProxyAuthCredentials( + proxy_server, scheme, realm, + base::BindLambdaForTesting( + [&](const base::Optional<net::AuthCredentials>& credentials) { + result = credentials; + run_loop.Quit(); + })); + run_loop.Run(); + return result; +} + +TEST_F(NetworkContextTest, LookupProxyAuthCredentials) { + GURL http_proxy("http://bar.test:1080"); + GURL https_proxy("https://bar.test:443"); + GURL http_proxy2("http://bar.test:443"); + GURL foo_proxy("foo://bar.test:1080"); + GURL server_origin("http://foo.test:3128"); + + std::unique_ptr<NetworkContext> network_context = + CreateContextWithParams(CreateContextParams()); + network_context->SetSplitAuthCacheByNetworkIsolationKey(true); + net::HttpAuthCache* cache = network_context->url_request_context() + ->http_transaction_factory() + ->GetSession() + ->http_auth_cache(); + + base::string16 user = base::ASCIIToUTF16("user"); + base::string16 password = base::ASCIIToUTF16("pass"); + cache->Add(http_proxy, net::HttpAuth::AUTH_PROXY, "Realm", + net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(), + "basic realm=Realm", net::AuthCredentials(user, password), + /* path = */ ""); + cache->Add(https_proxy, net::HttpAuth::AUTH_PROXY, "Realm", + net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(), + "basic realm=Realm", net::AuthCredentials(user, password), + /* path = */ ""); + cache->Add(server_origin, net::HttpAuth::AUTH_SERVER, "Realm", + net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey(), + "basic realm=Realm", net::AuthCredentials(user, password), + /* path = */ "/"); + base::Optional<net::AuthCredentials> result = GetProxyAuthCredentials( + network_context.get(), + net::ProxyServer(net::ProxyServer::Scheme::SCHEME_HTTP, + net::HostPortPair::FromURL(http_proxy)), + "bAsIc", "Realm"); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(user, result->username()); + EXPECT_EQ(password, result->password()); + + result = GetProxyAuthCredentials( + network_context.get(), + net::ProxyServer(net::ProxyServer::Scheme::SCHEME_HTTPS, + net::HostPortPair::FromURL(https_proxy)), + "bAsIc", "Realm"); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(user, result->username()); + EXPECT_EQ(password, result->password()); + + // Check that the proxy scheme is taken into account when looking for + // credentials + result = GetProxyAuthCredentials( + network_context.get(), + net::ProxyServer(net::ProxyServer::Scheme::SCHEME_HTTP, + net::HostPortPair::FromURL(http_proxy2)), + "basic", "Realm"); + EXPECT_FALSE(result.has_value()); + + // Check that the proxy authentication method is taken into account when + // looking for credentials + result = GetProxyAuthCredentials( + network_context.get(), + net::ProxyServer(net::ProxyServer::Scheme::SCHEME_HTTP, + net::HostPortPair::FromURL(http_proxy)), + "digest", "Realm"); + EXPECT_FALSE(result.has_value()); + + // Check that the realm is taken into account when looking for credentials + result = GetProxyAuthCredentials( + network_context.get(), + net::ProxyServer(net::ProxyServer::Scheme::SCHEME_HTTP, + net::HostPortPair::FromURL(http_proxy)), + "basic", "Realm 2"); + EXPECT_FALSE(result.has_value()); + + // All non-https proxies are cached as "http://" proxies + result = GetProxyAuthCredentials( + network_context.get(), + net::ProxyServer(net::ProxyServer::Scheme::SCHEME_HTTP, + net::HostPortPair::FromURL(foo_proxy)), + "basic", "Realm"); + EXPECT_FALSE(result.has_value()); + + // Server credentials should not be returned + result = GetProxyAuthCredentials( + network_context.get(), + net::ProxyServer(net::ProxyServer::Scheme::SCHEME_HTTP, + net::HostPortPair::FromURL(server_origin)), + "basic", "Realm"); + EXPECT_FALSE(result.has_value()); +} +#endif + #if BUILDFLAG(ENABLE_REPORTING) TEST_F(NetworkContextTest, ClearReportingCacheReports) { auto reporting_context = std::make_unique<net::TestReportingContext>( @@ -2264,8 +2374,8 @@ TEST_F(NetworkContextTest, ClearEmptyNetworkErrorLoggingWithNoService) { void SetCookieCallback(base::RunLoop* run_loop, bool* result_out, - net::CookieInclusionStatus result) { - *result_out = result.IsInclude(); + net::CookieAccessResult result) { + *result_out = result.status.IsInclude(); run_loop->Quit(); } @@ -2889,7 +2999,7 @@ TEST_F(NetworkContextTest, CreateNetLogExporterUnbounded) { // net:: methods. EXPECT_NE(std::string::npos, contents.find("ERR_IO_PENDING")) << contents; - base::DeleteFile(temp_path, false); + base::DeleteFile(temp_path); } TEST_F(NetworkContextTest, CreateNetLogExporterErrors) { @@ -2931,8 +3041,8 @@ TEST_F(NetworkContextTest, CreateNetLogExporterErrors) { net::NetLogCaptureMode::kDefault, 100 * 1024, start_callback2.callback()); EXPECT_EQ(net::ERR_UNEXPECTED, start_callback2.WaitForResult()); - base::DeleteFile(temp_path, false); - base::DeleteFile(temp_path2, false); + base::DeleteFile(temp_path); + base::DeleteFile(temp_path2); // Forgetting to stop is recovered from. } @@ -2979,7 +3089,7 @@ TEST_F(NetworkContextTest, DestroyNetLogExporterWhileCreatingScratchDir) { task_environment_.RunUntilIdle(); EXPECT_FALSE(base::PathExists(path)); - base::DeleteFile(temp_path, false); + base::DeleteFile(temp_path); } net::IPEndPoint CreateExpectedEndPoint(const std::string& address, @@ -4682,7 +4792,7 @@ TEST_F(NetworkContextTest, ForceReloadProxyConfig) { EXPECT_NE(std::string::npos, log_contents.find("\"new_config\"")) << log_contents; - base::DeleteFile(net_log_path, false); + base::DeleteFile(net_log_path); } TEST_F(NetworkContextTest, ClearBadProxiesCache) { @@ -5544,7 +5654,15 @@ class NetworkContextMockHostTest : public NetworkContextTest { } }; -TEST_F(NetworkContextMockHostTest, CustomProxyUsesSpecifiedProxyList) { +#if defined(OS_LINUX) +// Flaky crashes on Linux: https://crbug.com/1115201 +#define MAYBE_CustomProxyUsesSpecifiedProxyList \ + DISABLED_CustomProxyUsesSpecifiedProxyList +#else +#define MAYBE_CustomProxyUsesSpecifiedProxyList \ + CustomProxyUsesSpecifiedProxyList +#endif +TEST_F(NetworkContextMockHostTest, MAYBE_CustomProxyUsesSpecifiedProxyList) { net::EmbeddedTestServer proxy_test_server; net::test_server::RegisterDefaultHandlers(&proxy_test_server); ASSERT_TRUE(proxy_test_server.Start()); @@ -6620,32 +6738,6 @@ TEST_F(NetworkContextExpectBadMessageTest, } TEST_F(NetworkContextExpectBadMessageTest, - FailsTrustTokenBearingRequestFromInsecureContext) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature(features::kTrustTokens); - - std::unique_ptr<NetworkContext> network_context = - CreateContextWithParams(mojom::NetworkContextParams::New()); - - // Allow |network_context|'s Trust Tokens store time to initialize - // asynchronously, if necessary. - task_environment_.RunUntilIdle(); - - ASSERT_TRUE(network_context->trust_token_store()); - - ResourceRequest my_request; - my_request.request_initiator = - url::Origin::Create(GURL("http://insecure-initiator.com")); - my_request.trust_token_params = - OptionalTrustTokenParams(mojom::TrustTokenParams::New()); - - std::unique_ptr<TestURLLoaderClient> client = - FetchRequest(my_request, network_context.get()); - - AssertBadMessage(); -} - -TEST_F(NetworkContextExpectBadMessageTest, FailsTrustTokenRedemptionWhenForbidden) { base::test::ScopedFeatureList scoped_feature_list; scoped_feature_list.InitAndEnableFeature(features::kTrustTokens); @@ -6672,7 +6764,12 @@ TEST_F(NetworkContextExpectBadMessageTest, FetchRequest(my_request, network_context.get(), mojom::kURLLoadOptionNone, mojom::kBrowserProcessId, std::move(factory_params)); - AssertBadMessage(); + // TODO(crbug.com/1118183): This test's expectation is temporarily inverted + // since the the ReportBadMessage check is disabled pending investigating a + // number of false positives. Once this investigation is finished, we should + // flip the test back to expecting a bad message. + // AssertBadMessage(); + EXPECT_FALSE(got_bad_message_); } TEST_F(NetworkContextExpectBadMessageTest, @@ -6702,7 +6799,12 @@ TEST_F(NetworkContextExpectBadMessageTest, FetchRequest(my_request, network_context.get(), mojom::kURLLoadOptionNone, mojom::kBrowserProcessId, std::move(factory_params)); - AssertBadMessage(); + // TODO(crbug.com/1118183): This test's expectation is temporarily inverted + // since the the ReportBadMessage check is disabled pending investigating a + // number of false positives. Once this investigation is finished, we should + // flip the test back to expecting a bad message. + // AssertBadMessage(); + EXPECT_FALSE(got_bad_message_); } TEST_F(NetworkContextTest, diff --git a/chromium/services/network/network_sandbox_hook_linux.cc b/chromium/services/network/network_sandbox_hook_linux.cc index da2704dfeab..feb61a7316c 100644 --- a/chromium/services/network/network_sandbox_hook_linux.cc +++ b/chromium/services/network/network_sandbox_hook_linux.cc @@ -13,8 +13,8 @@ using sandbox::syscall_broker::MakeBrokerCommandSet; namespace network { -bool NetworkPreSandboxHook(service_manager::SandboxLinux::Options options) { - auto* instance = service_manager::SandboxLinux::GetInstance(); +bool NetworkPreSandboxHook(sandbox::policy::SandboxLinux::Options options) { + auto* instance = sandbox::policy::SandboxLinux::GetInstance(); // TODO(tsepez): remove universal permission under filesytem root. instance->StartBrokerProcess( @@ -29,7 +29,7 @@ bool NetworkPreSandboxHook(service_manager::SandboxLinux::Options options) { sandbox::syscall_broker::COMMAND_UNLINK, }), {BrokerFilePermission::ReadWriteCreateRecursive("/")}, - service_manager::SandboxLinux::PreSandboxHook(), options); + sandbox::policy::SandboxLinux::PreSandboxHook(), options); instance->EngageNamespaceSandboxIfPossible(); return true; diff --git a/chromium/services/network/network_sandbox_hook_linux.h b/chromium/services/network/network_sandbox_hook_linux.h index c398ac424c5..0c09336fc4d 100644 --- a/chromium/services/network/network_sandbox_hook_linux.h +++ b/chromium/services/network/network_sandbox_hook_linux.h @@ -6,12 +6,12 @@ #define SERVICES_NETWORK_NETWORK_SANDBOX_HOOK_LINUX_H_ #include "base/component_export.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" +#include "sandbox/policy/linux/sandbox_linux.h" namespace network { COMPONENT_EXPORT(NETWORK_SERVICE) -bool NetworkPreSandboxHook(service_manager::SandboxLinux::Options options); +bool NetworkPreSandboxHook(sandbox::policy::SandboxLinux::Options options); } // namespace network diff --git a/chromium/services/network/network_sandbox_win.cc b/chromium/services/network/network_sandbox_win.cc deleted file mode 100644 index 6fd36927899..00000000000 --- a/chromium/services/network/network_sandbox_win.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/network/network_sandbox_win.h" - -#include "sandbox/win/src/sandbox_types.h" -#include "services/service_manager/sandbox/win/sandbox_win.h" - -// NOTE: changes to this code need to be reviewed by the security team. -namespace network { - -// Right now, this policy is essentially unsandboxed, but with default process -// mitigations applied. This will be tighted up in future releases. -bool NetworkPreSpawnTarget(sandbox::TargetPolicy* policy, - const base::CommandLine& cmd_line) { - sandbox::ResultCode result = policy->SetTokenLevel(sandbox::USER_UNPROTECTED, - sandbox::USER_UNPROTECTED); - if (result != sandbox::ResultCode::SBOX_ALL_OK) - return false; - result = service_manager::SandboxWin::SetJobLevel( - cmd_line, sandbox::JOB_UNPROTECTED, 0, policy); - if (result != sandbox::ResultCode::SBOX_ALL_OK) - return false; - return true; -} - -} // namespace network diff --git a/chromium/services/network/network_sandbox_win.h b/chromium/services/network/network_sandbox_win.h deleted file mode 100644 index b2e68a3cafc..00000000000 --- a/chromium/services/network/network_sandbox_win.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_NETWORK_NETWORK_SANDBOX_WIN_H_ -#define SERVICES_NETWORK_NETWORK_SANDBOX_WIN_H_ - -#include "base/component_export.h" -#include "sandbox/win/src/sandbox_policy_base.h" -#include "services/service_manager/sandbox/win/sandbox_win.h" - -// These sandbox-config extension functions should be called from -// UtilitySandboxedProcessLauncherDelegate on Windows (or the appropriate -// Delegate if SandboxType::kNetwork is removed from SandboxType::kUtility). -// -// NOTE: changes to this code need to be reviewed by the security team. - -namespace network { - -// PreSpawnTarget extension. -COMPONENT_EXPORT(NETWORK_SERVICE) -bool NetworkPreSpawnTarget(sandbox::TargetPolicy* policy, - const base::CommandLine& cmd_line); - -} // namespace network - -#endif // SERVICES_NETWORK_NETWORK_SANDBOX_WIN_H_ diff --git a/chromium/services/network/network_service.cc b/chromium/services/network/network_service.cc index 75b0c7e2e97..dbd0830737b 100644 --- a/chromium/services/network/network_service.cc +++ b/chromium/services/network/network_service.cc @@ -48,7 +48,7 @@ #include "net/ssl/ssl_key_logger_impl.h" #include "net/url_request/url_request_context.h" #include "services/network/crl_set_distributor.h" -#include "services/network/cross_origin_read_blocking.h" +#include "services/network/cross_origin_read_blocking_exception_for_plugin.h" #include "services/network/dns_config_change_manager.h" #include "services/network/http_auth_cache_copier.h" #include "services/network/legacy_tls_config_distributor.h" @@ -57,10 +57,12 @@ #include "services/network/network_context.h" #include "services/network/network_usage_accumulator.h" #include "services/network/public/cpp/crash_keys.h" +#include "services/network/public/cpp/cross_origin_read_blocking.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/initiator_lock_compatibility.h" #include "services/network/public/cpp/load_info_util.h" #include "services/network/public/cpp/network_switches.h" +#include "services/network/sct_auditing_cache.h" #include "services/network/url_loader.h" #if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL) @@ -366,6 +368,12 @@ void NetworkService::Initialize(mojom::NetworkServiceParamsPtr params, doh_probe_activator_ = std::make_unique<DelayedDohProbeActivator>(this); trust_token_key_commitments_ = std::make_unique<TrustTokenKeyCommitments>(); + +#if BUILDFLAG(IS_CT_SUPPORTED) + constexpr size_t kMaxSCTAuditingCacheEntries = 1024; + sct_auditing_cache_ = + std::make_unique<SCTAuditingCache>(kMaxSCTAuditingCacheEntries); +#endif } NetworkService::~NetworkService() { @@ -672,7 +680,7 @@ void NetworkService::SetCryptConfig(mojom::CryptConfigPtr crypt_config) { } #endif -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) || defined(OS_MAC) void NetworkService::SetEncryptionKey(const std::string& encryption_key) { OSCrypt::SetRawEncryptionKey(encryption_key); } @@ -680,7 +688,7 @@ void NetworkService::SetEncryptionKey(const std::string& encryption_key) { void NetworkService::AddCorbExceptionForPlugin(int32_t process_id) { DCHECK_NE(mojom::kBrowserProcessId, process_id); - CrossOriginReadBlocking::AddExceptionForPlugin(process_id); + CrossOriginReadBlockingExceptionForPlugin::AddExceptionForPlugin(process_id); } void NetworkService::AddAllowedRequestInitiatorForPlugin( @@ -694,7 +702,8 @@ void NetworkService::AddAllowedRequestInitiatorForPlugin( void NetworkService::RemoveSecurityExceptionsForPlugin(int32_t process_id) { DCHECK_NE(mojom::kBrowserProcessId, process_id); - CrossOriginReadBlocking::RemoveExceptionForPlugin(process_id); + CrossOriginReadBlockingExceptionForPlugin::RemoveExceptionForPlugin( + process_id); std::map<int, std::set<url::Origin>>& map = plugin_origins_; map.erase(process_id); @@ -744,6 +753,12 @@ void NetworkService::SetTrustTokenKeyCommitments( std::move(done).Run(); } +#if BUILDFLAG(IS_CT_SUPPORTED) +void NetworkService::ClearSCTAuditingCache() { + sct_auditing_cache_->ClearCache(); +} +#endif + #if defined(OS_ANDROID) void NetworkService::DumpWithoutCrashing(base::Time dump_request_time) { static base::debug::CrashKeyString* time_key = diff --git a/chromium/services/network/network_service.h b/chromium/services/network/network_service.h index 50120d006a0..85dcae89361 100644 --- a/chromium/services/network/network_service.h +++ b/chromium/services/network/network_service.h @@ -62,6 +62,7 @@ class NetLogProxySink; class NetworkContext; class NetworkService; class NetworkUsageAccumulator; +class SCTAuditingCache; // DataPipeUseTracker tracks the mojo data pipe usage in the network // service. @@ -195,7 +196,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService #if defined(OS_LINUX) && !defined(OS_CHROMEOS) void SetCryptConfig(mojom::CryptConfigPtr crypt_config) override; #endif -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) || defined(OS_MAC) void SetEncryptionKey(const std::string& encryption_key) override; #endif void AddCorbExceptionForPlugin(int32_t process_id) override; @@ -213,6 +214,9 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService std::vector<mojom::EnvironmentVariablePtr> environment) override; void SetTrustTokenKeyCommitments(const std::string& raw_commitments, base::OnceClosure done) override; +#if BUILDFLAG(IS_CT_SUPPORTED) + void ClearSCTAuditingCache() override; +#endif #if defined(OS_ANDROID) void DumpWithoutCrashing(base::Time dump_request_time) override; @@ -283,6 +287,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService return trust_token_key_commitments_.get(); } +#if BUILDFLAG(IS_CT_SUPPORTED) + SCTAuditingCache* sct_auditing_cache() { return sct_auditing_cache_.get(); } +#endif + void OnDataPipeCreated(DataPipeUser user); void OnDataPipeDropped(DataPipeUser user); void StopMetricsTimerForTesting(); @@ -407,6 +415,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkService std::unique_ptr<DelayedDohProbeActivator> doh_probe_activator_; +#if BUILDFLAG(IS_CT_SUPPORTED) + std::unique_ptr<SCTAuditingCache> sct_auditing_cache_; +#endif + // Map from a renderer process id, to the set of plugin origins embedded by // that renderer process (the renderer will proxy requests from PPAPI - such // requests should have their initiator origin within the set stored here). diff --git a/chromium/services/network/network_service_network_delegate.cc b/chromium/services/network/network_service_network_delegate.cc index abf4451b269..8199b693b81 100644 --- a/chromium/services/network/network_service_network_delegate.cc +++ b/chromium/services/network/network_service_network_delegate.cc @@ -15,6 +15,7 @@ #include "net/base/isolation_info.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" +#include "net/url_request/referrer_policy.h" #include "net/url_request/url_request.h" #include "services/network/cookie_manager.h" #include "services/network/network_context.h" @@ -56,7 +57,7 @@ void NetworkServiceNetworkDelegate::MaybeTruncateReferrer( const GURL& effective_url) { if (!enable_referrers_) { request->SetReferrer(std::string()); - request->set_referrer_policy(net::URLRequest::NO_REFERRER); + request->set_referrer_policy(net::ReferrerPolicy::NO_REFERRER); return; } diff --git a/chromium/services/network/network_service_proxy_delegate_unittest.cc b/chromium/services/network/network_service_proxy_delegate_unittest.cc index a4a8c49612a..0519a1edf0a 100644 --- a/chromium/services/network/network_service_proxy_delegate_unittest.cc +++ b/chromium/services/network/network_service_proxy_delegate_unittest.cc @@ -103,8 +103,6 @@ TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxySuccessHttpProxy) { expected_proxy_list.AddProxyServer( net::ProxyServer::FromPacString("PROXY foo")); EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list)); - // HTTP proxies are not used as alternative QUIC proxies. - EXPECT_FALSE(result.alternative_proxy().is_valid()); } TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxySuccessHttpsUrl) { @@ -150,7 +148,6 @@ TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyNoRuleForHttpsUrl) { &result); EXPECT_TRUE(result.is_direct()); - EXPECT_FALSE(result.alternative_proxy().is_valid()); } TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyLocalhost) { @@ -164,7 +161,6 @@ TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyLocalhost) { &result); EXPECT_TRUE(result.is_direct()); - EXPECT_FALSE(result.alternative_proxy().is_valid()); } TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyEmptyConfig) { @@ -176,7 +172,6 @@ TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyEmptyConfig) { &result); EXPECT_TRUE(result.is_direct()); - EXPECT_FALSE(result.alternative_proxy().is_valid()); } TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyNonIdempotentMethod) { @@ -190,7 +185,6 @@ TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyNonIdempotentMethod) { &result); EXPECT_TRUE(result.is_direct()); - EXPECT_FALSE(result.alternative_proxy().is_valid()); } TEST_F(NetworkServiceProxyDelegateTest, @@ -225,7 +219,6 @@ TEST_F(NetworkServiceProxyDelegateTest, &result); EXPECT_TRUE(result.is_direct()); - EXPECT_FALSE(result.alternative_proxy().is_valid()); } TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyDoesNotOverrideExisting) { @@ -243,7 +236,6 @@ TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyDoesNotOverrideExisting) { expected_proxy_list.AddProxyServer( net::ProxyServer::FromPacString("PROXY bar")); EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list)); - EXPECT_FALSE(result.alternative_proxy().is_valid()); } TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyOverridesExisting) { @@ -261,7 +253,6 @@ TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyOverridesExisting) { expected_proxy_list.AddProxyServer( net::ProxyServer::FromPacString("PROXY foo")); EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list)); - EXPECT_FALSE(result.alternative_proxy().is_valid()); } TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyDeprioritizesBadProxies) { diff --git a/chromium/services/network/network_service_unittest.cc b/chromium/services/network/network_service_unittest.cc index bb38b299fcb..20d7ed664ce 100644 --- a/chromium/services/network/network_service_unittest.cc +++ b/chromium/services/network/network_service_unittest.cc @@ -935,6 +935,8 @@ class NetworkServiceTestWithService : public testing::Test { mojom::URLLoaderFactoryParamsPtr params = mojom::URLLoaderFactoryParams::New(); params->process_id = process_id; + params->request_initiator_origin_lock = + url::Origin::Create(GURL("https://initiator.example.com")); params->is_corb_enabled = false; network_context_->CreateURLLoaderFactory( loader_factory.BindNewPipeAndPassReceiver(), std::move(params)); diff --git a/chromium/services/network/proxy_resolving_client_socket_factory.cc b/chromium/services/network/proxy_resolving_client_socket_factory.cc index 47d01a340d3..caa2d52a71d 100644 --- a/chromium/services/network/proxy_resolving_client_socket_factory.cc +++ b/chromium/services/network/proxy_resolving_client_socket_factory.cc @@ -29,6 +29,8 @@ ProxyResolvingClientSocketFactory::ProxyResolvingClientSocketFactory( session_context.cert_transparency_verifier = request_context->cert_transparency_verifier(); session_context.ct_policy_enforcer = request_context->ct_policy_enforcer(); + session_context.sct_auditing_delegate = + request_context->sct_auditing_delegate(); session_context.proxy_resolution_service = request_context->proxy_resolution_service(); session_context.proxy_delegate = request_context->proxy_delegate(); diff --git a/chromium/services/network/public/cpp/BUILD.gn b/chromium/services/network/public/cpp/BUILD.gn index 8447695610e..039da2fdc70 100644 --- a/chromium/services/network/public/cpp/BUILD.gn +++ b/chromium/services/network/public/cpp/BUILD.gn @@ -3,7 +3,6 @@ # found in the LICENSE file. import("//build/buildflag_header.gni") -import("//build/config/jumbo.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//services/network/public/cpp/features.gni") import("//testing/libfuzzer/fuzzer_test.gni") @@ -13,7 +12,7 @@ buildflag_header("buildflags") { flags = [ "IS_CT_SUPPORTED=$is_ct_supported" ] } -jumbo_component("crash_keys") { +component("crash_keys") { sources = [ "crash_keys.cc", "crash_keys.h", @@ -22,7 +21,7 @@ jumbo_component("crash_keys") { defines = [ "IS_NETWORK_CPP_CRASH_KEYS_IMPL" ] } -jumbo_component("cpp") { +component("cpp") { output_name = "network_cpp" sources = [ @@ -52,6 +51,8 @@ jumbo_component("cpp") { "cross_origin_embedder_policy_parser.h", "cross_origin_opener_policy_parser.cc", "cross_origin_opener_policy_parser.h", + "cross_origin_read_blocking.cc", + "cross_origin_read_blocking.h", "cross_origin_resource_policy.cc", "cross_origin_resource_policy.h", "cross_thread_pending_shared_url_loader_factory.cc", @@ -66,6 +67,8 @@ jumbo_component("cpp") { "header_util.h", "initiator_lock_compatibility.cc", "initiator_lock_compatibility.h", + "ip_address_space_util.cc", + "ip_address_space_util.h", "is_potentially_trustworthy.cc", "is_potentially_trustworthy.h", "load_info_util.cc", @@ -78,6 +81,8 @@ jumbo_component("cpp") { "network_quality_tracker.h", "network_switches.cc", "network_switches.h", + "not_implemented_url_loader_factory.cc", + "not_implemented_url_loader_factory.h", "origin_isolation_parser.cc", "origin_isolation_parser.h", "parsed_headers.cc", @@ -88,6 +93,7 @@ jumbo_component("cpp") { "request_mode.h", "resolve_host_client_base.cc", "resolve_host_client_base.h", + "session_cookie_delete_predicate.h", "shared_url_loader_factory.cc", "shared_url_loader_factory.h", "simple_url_loader.cc", @@ -135,6 +141,7 @@ jumbo_component("cpp") { deps = [ "//base", + "//base/util/ranges:ranges", "//components/prefs", "//ipc", "//services/proxy_resolver/public/mojom", @@ -174,7 +181,7 @@ component("cookies_mojom_support") { defines = [ "IS_NETWORK_CPP_BASE_IMPL" ] } -jumbo_component("cpp_base") { +component("cpp_base") { output_name = "network_cpp_base" sources = [ @@ -231,15 +238,6 @@ jumbo_component("cpp_base") { "url_request_mojom_traits.cc", "url_request_mojom_traits.h", ] - jumbo_excluded_sources = [ - # IPC/Params code generators are based on macros and multiple - # inclusion of headers using those macros. That is not - # compatible with jumbo compiling all source, generators and - # users, together, so exclude those files from jumbo compilation. - "network_ipc_param_traits.cc", - "p2p_param_traits.cc", - "url_request_mojom_traits.cc", - ] configs += [ "//build/config/compiler:wexit_time_destructors" ] @@ -250,6 +248,7 @@ jumbo_component("cpp_base") { "//services/network/public/mojom:data_pipe_interfaces", "//services/network/public/mojom:mutable_network_traffic_annotation_interface", "//services/network/public/mojom:trust_tokens_interface", + "//third_party/webrtc_overrides:webrtc_component", "//url/ipc:url_ipc", "//url/mojom:url_mojom_gurl", "//url/mojom:url_mojom_origin", @@ -261,7 +260,6 @@ jumbo_component("cpp_base") { "//net", "//services/network/public/mojom:cookies_mojom", "//services/network/public/mojom:mojom_shared", - "//third_party/webrtc_overrides:webrtc_component", ] defines = [ "IS_NETWORK_CPP_BASE_IMPL" ] } @@ -288,6 +286,7 @@ source_set("tests") { "cors/preflight_result_unittest.cc", "cross_origin_embedder_policy_parser_unittest.cc", "cross_origin_opener_policy_parser_unittest.cc", + "cross_origin_read_blocking_unittest.cc", "cross_origin_resource_policy_unittest.cc", "cross_thread_pending_shared_url_loader_factory_unittest.cc", "data_pipe_to_source_stream_unittest.cc", @@ -297,6 +296,7 @@ source_set("tests") { "host_resolver_mojom_traits_unittest.cc", "initiator_lock_compatibility_unittest.cc", "ip_address_mojom_traits_unittest.cc", + "ip_address_space_util_unittest.cc", "is_potentially_trustworthy_unittest.cc", "isolation_info_mojom_traits_unittest.cc", "mutable_network_traffic_annotation_tag_mojom_traits_unittest.cc", diff --git a/chromium/services/network/public/cpp/cert_verifier/trial_comparison_cert_verifier_mojo.cc b/chromium/services/network/public/cpp/cert_verifier/trial_comparison_cert_verifier_mojo.cc index d6ca262a6a8..50c45e554ab 100644 --- a/chromium/services/network/public/cpp/cert_verifier/trial_comparison_cert_verifier_mojo.cc +++ b/chromium/services/network/public/cpp/cert_verifier/trial_comparison_cert_verifier_mojo.cc @@ -13,7 +13,7 @@ #include "net/der/encode_values.h" #include "net/der/parse_values.h" -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MAC) #include "net/cert/cert_verify_proc_mac.h" #include "net/cert/internal/trust_store_mac.h" #endif @@ -74,7 +74,7 @@ void TrialComparisonCertVerifierMojo::OnSendTrialReport( const net::CertVerifyResult& trial_result) { network::mojom::CertVerifierDebugInfoPtr debug_info = network::mojom::CertVerifierDebugInfo::New(); -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MAC) auto* mac_platform_debug_info = net::CertVerifyProcMac::ResultDebugData::Get(&primary_result); if (mac_platform_debug_info) { diff --git a/chromium/services/network/public/cpp/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc b/chromium/services/network/public/cpp/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc index f349c1f4497..73a5200e1d2 100644 --- a/chromium/services/network/public/cpp/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc +++ b/chromium/services/network/public/cpp/cert_verifier/trial_comparison_cert_verifier_mojo_unittest.cc @@ -15,7 +15,7 @@ #include "net/test/test_data_directory.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MAC) #include "net/cert/cert_verify_proc_mac.h" #include "net/cert/internal/trust_store_mac.h" #endif @@ -104,7 +104,7 @@ TEST(TrialComparisonCertVerifierMojoTest, SendReportDebugInfo) { net::CertVerifyResult trial_result; trial_result.verified_cert = chain2; -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MAC) constexpr uint32_t kExpectedTrustResult = 4; constexpr int32_t kExpectedResultCode = -12345; std::vector<net::CertVerifyProcMac::ResultDebugData::CertEvidenceInfo> @@ -169,7 +169,7 @@ TEST(TrialComparisonCertVerifierMojoTest, SendReportDebugInfo) { std::string(report.sct_list.begin(), report.sct_list.end())); ASSERT_TRUE(report.debug_info); -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MAC) ASSERT_TRUE(report.debug_info->mac_platform_debug_info); EXPECT_EQ(kExpectedTrustResult, report.debug_info->mac_platform_debug_info->trust_result); diff --git a/chromium/services/network/public/cpp/client_hints.cc b/chromium/services/network/public/cpp/client_hints.cc index ab9b33e268e..0958d28b3ab 100644 --- a/chromium/services/network/public/cpp/client_hints.cc +++ b/chromium/services/network/public/cpp/client_hints.cc @@ -11,6 +11,7 @@ #include "base/no_destructor.h" #include "base/optional.h" #include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" #include "net/http/structured_headers.h" @@ -104,4 +105,13 @@ base::Optional<std::vector<network::mojom::WebClientHintsType>> ParseAcceptCH( return base::make_optional(std::move(result)); } +base::TimeDelta ParseAcceptCHLifetime(const std::string& header) { + int64_t persist_duration_seconds = 0; + if (!base::StringToInt64(header, &persist_duration_seconds) || + persist_duration_seconds <= 0) + return base::TimeDelta(); + + return base::TimeDelta::FromSeconds(persist_duration_seconds); +} + } // namespace network diff --git a/chromium/services/network/public/cpp/client_hints.h b/chromium/services/network/public/cpp/client_hints.h index 8f8db9548e1..a811582df39 100644 --- a/chromium/services/network/public/cpp/client_hints.h +++ b/chromium/services/network/public/cpp/client_hints.h @@ -10,6 +10,7 @@ #include "base/component_export.h" #include "base/optional.h" +#include "base/time/time.h" #include "services/network/public/mojom/web_client_hints_types.mojom-shared.h" namespace network { @@ -27,6 +28,10 @@ COMPONENT_EXPORT(NETWORK_CPP) extern const size_t kClientHintsMappingsCount; base::Optional<std::vector<network::mojom::WebClientHintsType>> COMPONENT_EXPORT(NETWORK_CPP) ParseAcceptCH(const std::string& header); +// Tries to parse Accept-CH-Lifetime. Returns base::TimeDelta() if unsuccessful. +base::TimeDelta COMPONENT_EXPORT(NETWORK_CPP) + ParseAcceptCHLifetime(const std::string& header); + } // namespace network #endif // SERVICES_NETWORK_PUBLIC_CPP_CLIENT_HINTS_H_ diff --git a/chromium/services/network/public/cpp/client_hints_unittest.cc b/chromium/services/network/public/cpp/client_hints_unittest.cc index e1e4d2ceea4..3a2c0bbf1be 100644 --- a/chromium/services/network/public/cpp/client_hints_unittest.cc +++ b/chromium/services/network/public/cpp/client_hints_unittest.cc @@ -67,4 +67,12 @@ TEST(ClientHintsTest, ParseAcceptCHCaseInsensitive) { network::mojom::WebClientHintsType::kLang)); } +TEST(ClientHintsTest, ParseAcceptCHLifetime) { + EXPECT_EQ(base::TimeDelta(), ParseAcceptCHLifetime("")); + EXPECT_EQ(base::TimeDelta(), ParseAcceptCHLifetime("-1000")); + EXPECT_EQ(base::TimeDelta(), ParseAcceptCHLifetime("1000s")); + EXPECT_EQ(base::TimeDelta(), ParseAcceptCHLifetime("1000.5")); + EXPECT_EQ(base::TimeDelta::FromSeconds(1000), ParseAcceptCHLifetime("1000")); +} + } // namespace network diff --git a/chromium/services/network/public/cpp/content_security_policy/content_security_policy.cc b/chromium/services/network/public/cpp/content_security_policy/content_security_policy.cc index 6ae235de565..914a7871b47 100644 --- a/chromium/services/network/public/cpp/content_security_policy/content_security_policy.cc +++ b/chromium/services/network/public/cpp/content_security_policy/content_security_policy.cc @@ -6,56 +6,104 @@ #include <sstream> #include <string> -#include "base/containers/flat_map.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/util/ranges/algorithm.h" #include "net/http/http_response_headers.h" #include "services/network/public/cpp/content_security_policy/csp_context.h" #include "services/network/public/cpp/content_security_policy/csp_source.h" #include "services/network/public/cpp/content_security_policy/csp_source_list.h" +#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" #include "services/network/public/cpp/web_sandbox_flags.h" #include "url/gurl.h" +#include "url/origin.h" #include "url/url_canon.h" #include "url/url_util.h" namespace network { using CSPDirectiveName = mojom::CSPDirectiveName; -using DirectivesMap = base::flat_map<base::StringPiece, base::StringPiece>; +using DirectivesMap = + std::vector<std::pair<base::StringPiece, base::StringPiece>>; namespace { -static CSPDirectiveName CSPFallback(CSPDirectiveName directive) { +bool IsDirectiveNameCharacter(char c) { + return base::IsAsciiAlpha(c) || c == '-'; +} + +bool IsDirectiveValueCharacter(char c) { + return base::IsAsciiWhitespace(c) || + base::IsAsciiPrintable(c); // Whitespace + VCHAR +} + +static CSPDirectiveName CSPFallback(CSPDirectiveName directive, + CSPDirectiveName original_directive) { switch (directive) { - case CSPDirectiveName::DefaultSrc: - case CSPDirectiveName::FormAction: - case CSPDirectiveName::NavigateTo: - case CSPDirectiveName::FrameAncestors: + case CSPDirectiveName::ConnectSrc: + case CSPDirectiveName::FontSrc: case CSPDirectiveName::ImgSrc: + case CSPDirectiveName::ManifestSrc: case CSPDirectiveName::MediaSrc: + case CSPDirectiveName::PrefetchSrc: case CSPDirectiveName::ObjectSrc: case CSPDirectiveName::ScriptSrc: case CSPDirectiveName::StyleSrc: - case CSPDirectiveName::WorkerSrc: - case CSPDirectiveName::ConnectSrc: - return CSPDirectiveName::Unknown; + return CSPDirectiveName::DefaultSrc; + + case CSPDirectiveName::ScriptSrcAttr: + case CSPDirectiveName::ScriptSrcElem: + return CSPDirectiveName::ScriptSrc; + + case CSPDirectiveName::StyleSrcAttr: + case CSPDirectiveName::StyleSrcElem: + return CSPDirectiveName::StyleSrc; case CSPDirectiveName::FrameSrc: + case CSPDirectiveName::WorkerSrc: return CSPDirectiveName::ChildSrc; + // Because the fallback chain of child-src can be different if we are + // checking a worker or a frame request, we need to know the original type + // of the request to decide. These are the fallback chains for worker-src + // and frame-src specifically. + + // worker-src > child-src > script-src > default-src + // frame-src > child-src > default-src + + // Since there are some situations and tests that will operate on the + // `child-src` directive directly (like for example the EE subsumption + // algorithm), we consider the child-src > default-src fallback path as the + // "default" and the worker-src fallback path as an exception. case CSPDirectiveName::ChildSrc: + if (original_directive == CSPDirectiveName::WorkerSrc) + return CSPDirectiveName::ScriptSrc; + return CSPDirectiveName::DefaultSrc; + case CSPDirectiveName::BaseURI: + case CSPDirectiveName::BlockAllMixedContent: + case CSPDirectiveName::DefaultSrc: + case CSPDirectiveName::FormAction: + case CSPDirectiveName::FrameAncestors: + case CSPDirectiveName::NavigateTo: + case CSPDirectiveName::ReportTo: + case CSPDirectiveName::ReportURI: + case CSPDirectiveName::RequireTrustedTypesFor: + case CSPDirectiveName::Sandbox: + case CSPDirectiveName::TreatAsPublicAddress: + case CSPDirectiveName::TrustedTypes: + case CSPDirectiveName::UpgradeInsecureRequests: + return CSPDirectiveName::Unknown; case CSPDirectiveName::Unknown: NOTREACHED(); return CSPDirectiveName::Unknown; } - NOTREACHED(); - return CSPDirectiveName::Unknown; } std::string ElideURLForReportViolation(const GURL& url) { @@ -82,16 +130,32 @@ const char* ErrorMessage(CSPDirectiveName directive) { return "Refused to navigate to '$1' because it violates the " "following Content Security Policy directive: \"$2\"."; + case CSPDirectiveName::BaseURI: + case CSPDirectiveName::BlockAllMixedContent: case CSPDirectiveName::ChildSrc: + case CSPDirectiveName::ConnectSrc: case CSPDirectiveName::DefaultSrc: - case CSPDirectiveName::Unknown: + case CSPDirectiveName::FontSrc: case CSPDirectiveName::ImgSrc: + case CSPDirectiveName::ManifestSrc: case CSPDirectiveName::MediaSrc: case CSPDirectiveName::ObjectSrc: + case CSPDirectiveName::PrefetchSrc: + case CSPDirectiveName::ReportTo: + case CSPDirectiveName::ReportURI: + case CSPDirectiveName::RequireTrustedTypesFor: + case CSPDirectiveName::Sandbox: case CSPDirectiveName::ScriptSrc: + case CSPDirectiveName::ScriptSrcAttr: + case CSPDirectiveName::ScriptSrcElem: case CSPDirectiveName::StyleSrc: + case CSPDirectiveName::StyleSrcAttr: + case CSPDirectiveName::StyleSrcElem: + case CSPDirectiveName::TreatAsPublicAddress: + case CSPDirectiveName::TrustedTypes: + case CSPDirectiveName::UpgradeInsecureRequests: case CSPDirectiveName::WorkerSrc: - case CSPDirectiveName::ConnectSrc: + case CSPDirectiveName::Unknown: NOTREACHED(); return nullptr; }; @@ -179,21 +243,16 @@ DirectivesMap ParseHeaderValue(base::StringPiece header) { size_t pos = directive.find_first_of(base::kWhitespaceASCII); base::StringPiece name = directive.substr(0, pos); - // 5. If policy's directive set contains a directive whose name is - // directive name, continue. - if (result.find(name) != result.end()) - continue; - - // 6. Let directive value be the result of splitting token on ASCII + // 5. Let directive value be the result of splitting token on ASCII // whitespace. base::StringPiece value; if (pos != std::string::npos) value = directive.substr(pos + 1); - // 7. Let directive be a new directive whose name is directive name, + // 6. Let directive be a new directive whose name is directive name, // and value is directive value. - // 8. Append directive to policy's directive set. - result.insert({name, value}); + // 7. Append directive to policy's directive set. + result.emplace_back(std::make_pair(name, value)); } return result; @@ -278,10 +337,6 @@ bool ParsePath(base::StringPiece path, mojom::CSPSource* csp_source) { if (path[0] != '/') return false; - // TODO(lfg): Emit a warning to the user when a path containing # or ? is - // seen. - path = path.substr(0, path.find_first_of("#?")); - url::RawCanonOutputT<base::char16> unescaped; url::DecodeURLEscapeSequences(path.data(), path.size(), url::DecodeURLMode::kUTF8OrIsomorphic, @@ -291,17 +346,17 @@ bool ParsePath(base::StringPiece path, mojom::CSPSource* csp_source) { return true; } -// Parses an ancestor source expression. -// https://www.w3.org/TR/CSP3/#grammardef-ancestor-source +// Parses a CSP source expression. +// https://w3c.github.io/webappsec-csp/#source-lists // // Return false on errors. -bool ParseAncestorSource(base::StringPiece expression, - mojom::CSPSource* csp_source) { - // TODO(arthursonzogni): Blink reports an invalid source expression when - // 'none' is parsed here. - if (base::EqualsCaseInsensitiveASCII(expression, "'none'")) - return false; - +// Adds parsing error messages to |parsing_errors|. +// Notice that this can return true and still add some parsing error message +// (for example, if there is a url with a non-empty query part). +bool ParseSource(CSPDirectiveName directive_name, + base::StringPiece expression, + mojom::CSPSource* csp_source, + std::vector<std::string>& parsing_errors) { size_t position = expression.find_first_of(":/"); if (position != std::string::npos && expression[position] == ':') { // scheme: @@ -348,15 +403,125 @@ bool ParseAncestorSource(base::StringPiece expression, // / // ^ - return expression.empty() || ParsePath(expression, csp_source); + if (expression.empty()) + return true; + + // Emit a warning to the user when a url contains a # or ?. + position = expression.find_first_of("#?"); + bool path_parsed = ParsePath(expression.substr(0, position), csp_source); + if (path_parsed && position != std::string::npos) { + const char* ignoring = + expression[position] == '?' + ? "The query component, including the '?', will be ignored." + : "The fragment identifier, including the '#', will be ignored."; + parsing_errors.emplace_back(base::StringPrintf( + "The source list for Content-Security-Policy directive '%s' " + "contains a source with an invalid path: '%s'. %s", + ToString(directive_name).c_str(), expression.as_string().c_str(), + ignoring)); + } + + return path_parsed; +} + +bool IsBase64Char(char c) { + return base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) || c == '+' || + c == '-' || c == '_' || c == '/'; +} + +int EatChar(const char** it, const char* end, bool (*predicate)(char)) { + int count = 0; + while (*it != end) { + if (!predicate(**it)) + break; + ++count; + ++(*it); + } + return count; +} + +// Checks whether |expression| is a valid base64-encoded string. +// Cf. https://w3c.github.io/webappsec-csp/#framework-directive-source-list. +bool IsBase64(base::StringPiece expression) { + if (expression.empty()) + return false; + + auto* it = expression.begin(); + auto* end = expression.end(); + + int count_1 = EatChar(&it, end, IsBase64Char); + int count_2 = EatChar(&it, end, [](char c) -> bool { return c == '='; }); + + // At least one non '=' char at the beginning, at most two '=' at the end. + return count_1 >= 1 && count_2 <= 2 && it == end; +} + +// Parse a nonce-source, return false on error. +bool ParseNonce(base::StringPiece expression, std::string* nonce) { + if (!base::StartsWith(expression, "'nonce-", + base::CompareCase::INSENSITIVE_ASCII)) { + return false; + } + + base::StringPiece subexpression = + expression.substr(7, expression.length() - 8); + + if (!IsBase64(subexpression)) + return false; + + if (expression[expression.length() - 1] != '\'') { + return false; + } + + *nonce = subexpression.as_string(); + return true; +} + +struct SupportedPrefixesStruct { + const char* prefix; + int prefix_length; + mojom::CSPHashAlgorithm type; +}; + +// Parse a hash-source, return false on error. +bool ParseHash(base::StringPiece expression, mojom::CSPHashSource* hash) { + static const SupportedPrefixesStruct SupportedPrefixes[] = { + {"'sha256-", 8, mojom::CSPHashAlgorithm::SHA256}, + {"'sha384-", 8, mojom::CSPHashAlgorithm::SHA384}, + {"'sha512-", 8, mojom::CSPHashAlgorithm::SHA512}, + {"'sha-256-", 9, mojom::CSPHashAlgorithm::SHA256}, + {"'sha-384-", 9, mojom::CSPHashAlgorithm::SHA384}, + {"'sha-512-", 9, mojom::CSPHashAlgorithm::SHA512}}; + + for (auto item : SupportedPrefixes) { + if (base::StartsWith(expression, item.prefix, + base::CompareCase::INSENSITIVE_ASCII)) { + base::StringPiece subexpression = expression.substr( + item.prefix_length, expression.length() - item.prefix_length - 1); + if (!IsBase64(subexpression)) + return false; + + if (expression[expression.length() - 1] != '\'') + return false; + + hash->algorithm = item.type; + hash->value = subexpression.as_string(); + return true; + } + } + + return false; } -// Parse ancestor-source-list grammar. -// https://www.w3.org/TR/CSP3/#directive-frame-ancestors -mojom::CSPSourceListPtr ParseFrameAncestorsSourceList( - base::StringPiece frame_ancestors_value) { - base::StringPiece value = base::TrimString( - frame_ancestors_value, base::kWhitespaceASCII, base::TRIM_ALL); +// Parse source-list grammar. +// https://www.w3.org/TR/CSP3/#grammardef-serialized-source-list +// Append parsing errors to |parsing_errors|. +mojom::CSPSourceListPtr ParseSourceList( + CSPDirectiveName directive_name, + base::StringPiece directive_value, + std::vector<std::string>& parsing_errors) { + base::StringPiece value = + base::TrimString(directive_value, base::kWhitespaceASCII, base::TRIM_ALL); auto directive = mojom::CSPSourceList::New(); @@ -366,6 +531,16 @@ mojom::CSPSourceListPtr ParseFrameAncestorsSourceList( for (const auto& expression : base::SplitStringPiece( value, base::kWhitespaceASCII, base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { + if (base::EqualsCaseInsensitiveASCII(expression, "'none'")) { + parsing_errors.emplace_back(base::StringPrintf( + "The Content-Security-Policy directive '%s' contains the keyword " + "'none' alongside with other source expressions. The keyword 'none' " + "must be the only source expression in the directive value, " + "otherwise it is ignored.", + ToString(directive_name).c_str())); + continue; + } + if (base::EqualsCaseInsensitiveASCII(expression, "'self'")) { directive->allow_self = true; continue; @@ -376,16 +551,87 @@ mojom::CSPSourceListPtr ParseFrameAncestorsSourceList( continue; } + if (ToCSPDirectiveName(expression.as_string()) != + CSPDirectiveName::Unknown) { + parsing_errors.emplace_back(base::StringPrintf( + "The Content-Security-Policy directive '%s' contains '%s' as a " + "source expression. Did you want to add it as a directive and forget " + "a semicolon?", + ToString(directive_name).c_str(), expression.as_string().c_str())); + } + auto csp_source = mojom::CSPSource::New(); - if (ParseAncestorSource(expression, csp_source.get())) { + if (ParseSource(directive_name, expression, csp_source.get(), + parsing_errors)) { directive->sources.push_back(std::move(csp_source)); continue; } + if (directive_name == CSPDirectiveName::FrameAncestors) { + // The frame-ancestors directive does not support anything else + // https://w3c.github.io/webappsec-csp/#directive-frame-ancestors + parsing_errors.emplace_back(base::StringPrintf( + "The Content-Security-Policy directive 'frame-ancestors' does not " + "support the source expression '%s'", + expression.as_string().c_str())); + continue; + } + + if (base::EqualsCaseInsensitiveASCII(expression, "'unsafe-inline'")) { + directive->allow_inline = true; + continue; + } + + if (base::EqualsCaseInsensitiveASCII(expression, "'unsafe-eval'")) { + directive->allow_eval = true; + continue; + } + + if (base::EqualsCaseInsensitiveASCII(expression, "'wasm-eval'")) { + directive->allow_wasm_eval = true; + continue; + } + + if (base::EqualsCaseInsensitiveASCII(expression, + "'unsafe-allow-redirects'") && + directive_name == CSPDirectiveName::NavigateTo) { + directive->allow_response_redirects = true; + continue; + } + + if (base::EqualsCaseInsensitiveASCII(expression, "'strict-dynamic'")) { + directive->allow_dynamic = true; + continue; + } + + if (base::EqualsCaseInsensitiveASCII(expression, "'unsafe-hashes'")) { + directive->allow_unsafe_hashes = true; + continue; + } + + if (base::EqualsCaseInsensitiveASCII(expression, "'report-sample'")) { + directive->report_sample = true; + continue; + } + + std::string nonce; + if (ParseNonce(expression, &nonce)) { + directive->nonces.push_back(std::move(nonce)); + continue; + } + + auto hash = mojom::CSPHashSource::New(); + if (ParseHash(expression, hash.get())) { + directive->hashes.push_back(std::move(hash)); + continue; + } + // Parsing error. // Ignore this source-expression. - // TODO(lfg): Emit a warning to the user when parsing an invalid - // expression. + parsing_errors.emplace_back(base::StringPrintf( + "The source list for the Content-Security-Policy directive '%s' " + "contains an invalid source: '%s'.", + ToString(directive_name).c_str(), expression.as_string().c_str())); } return directive; @@ -428,38 +674,132 @@ void ParseReportDirective(const GURL& request_url, } } -// Parses the frame-ancestor directive of a Content-Security-Policy header. -void ParseFrameAncestors(const mojom::ContentSecurityPolicyPtr& policy, - base::StringPiece frame_ancestors_value) { - // A frame-ancestors directive has already been parsed. Skip further - // frame-ancestors directives per - // https://www.w3.org/TR/CSP3/#parse-serialized-policy. - // TODO(arthursonzogni, lfg): Should a warning be fired to the user here? - if (policy->directives.count(CSPDirectiveName::FrameAncestors)) - return; - - auto source_list = ParseFrameAncestorsSourceList(frame_ancestors_value); +void AddContentSecurityPolicyFromHeader(base::StringPiece header, + mojom::ContentSecurityPolicyType type, + const GURL& base_url, + mojom::ContentSecurityPolicyPtr& out) { + DirectivesMap directives = ParseHeaderValue(header); + out->header = mojom::ContentSecurityPolicyHeader::New( + header.as_string(), type, mojom::ContentSecurityPolicySource::kHTTP); + + for (auto directive : directives) { + if (!util::ranges::all_of(directive.first, IsDirectiveNameCharacter)) { + out->parsing_errors.emplace_back(base::StringPrintf( + "The Content-Security-Policy directive name '%s' contains one or " + "more invalid characters. Only ASCII alphanumeric characters or " + "dashes '-' are allowed in directive names.", + directive.first.as_string().c_str())); + continue; + } - // TODO(lfg): Emit a warning to the user when parsing an invalid - // expression. - if (!source_list) - return; + if (!util::ranges::all_of(directive.second, IsDirectiveValueCharacter)) { + out->parsing_errors.emplace_back(base::StringPrintf( + "The value for the Content-Security-Policy directive '%s' contains " + "one or more invalid characters. Non-whitespace characters outside " + "ASCII 0x21-0x7E must be percent-encoded, as described in RFC 3986, " + "section 2.1: http://tools.ietf.org/html/rfc3986#section-2.1.", + directive.first.as_string().c_str())); + continue; + } - policy->directives[CSPDirectiveName::FrameAncestors] = std::move(source_list); -} + CSPDirectiveName directive_name = + ToCSPDirectiveName(directive.first.as_string()); -// Parses the report-uri directive of a Content-Security-Policy header. -void ParseReportEndpoint(const mojom::ContentSecurityPolicyPtr& policy, - const GURL& base_url, - base::StringPiece header_value, - bool using_reporting_api) { - // A report-uri directive has already been parsed. Skip further directives per - // https://www.w3.org/TR/CSP3/#parse-serialized-policy. - if (!policy->report_endpoints.empty()) - return; + // A directive with this name has already been parsed. Skip further + // directives per + // https://www.w3.org/TR/CSP3/#parse-serialized-policy. + if (out->directives.count(directive_name)) { + out->parsing_errors.emplace_back(base::StringPrintf( + "Ignoring duplicate Content-Security-Policy directive '%s'.", + directive.first.as_string().c_str())); + continue; + } - ParseReportDirective(base_url, header_value, using_reporting_api, - &(policy->report_endpoints)); + switch (directive_name) { + case CSPDirectiveName::BaseURI: + case CSPDirectiveName::ChildSrc: + case CSPDirectiveName::ConnectSrc: + case CSPDirectiveName::DefaultSrc: + case CSPDirectiveName::FontSrc: + case CSPDirectiveName::FormAction: + case CSPDirectiveName::FrameAncestors: + case CSPDirectiveName::FrameSrc: + case CSPDirectiveName::ImgSrc: + case CSPDirectiveName::ManifestSrc: + case CSPDirectiveName::MediaSrc: + case CSPDirectiveName::NavigateTo: + case CSPDirectiveName::ObjectSrc: + case CSPDirectiveName::PrefetchSrc: + case CSPDirectiveName::ScriptSrc: + case CSPDirectiveName::ScriptSrcAttr: + case CSPDirectiveName::ScriptSrcElem: + case CSPDirectiveName::StyleSrc: + case CSPDirectiveName::StyleSrcAttr: + case CSPDirectiveName::StyleSrcElem: + case CSPDirectiveName::WorkerSrc: + out->directives[directive_name] = ParseSourceList( + directive_name, directive.second, out->parsing_errors); + break; + case CSPDirectiveName::Sandbox: + // Note: |ParseSandboxPolicy(...).error_message| is ignored here. + // Blink's CSP parser is already in charge of displaying it. + { + auto sandbox = ParseWebSandboxPolicy(directive.second, + mojom::WebSandboxFlags::kNone); + out->sandbox = ~sandbox.flags; + out->parsing_errors.emplace_back(std::move(sandbox.error_message)); + } + break; + case CSPDirectiveName::UpgradeInsecureRequests: + out->upgrade_insecure_requests = true; + if (!directive.second.empty()) { + out->parsing_errors.emplace_back(base::StringPrintf( + "The Content Security Policy directive " + "'upgrade-insecure-requests' should be empty, but was delivered " + "with a value of '%s'. The directive has been applied, and the " + "value ignored.", + directive.second.as_string().c_str())); + } + break; + case CSPDirectiveName::TreatAsPublicAddress: + out->treat_as_public_address = true; + if (!directive.second.empty()) { + out->parsing_errors.emplace_back(base::StringPrintf( + "The Content Security Policy directive 'treat-as-public-address' " + "should be empty, but was delivered with a value of '%s'. The " + "directive has been applied, and the value ignored.", + directive.second.as_string().c_str())); + } + break; + + // We check the following three directives so that we do not trigger a + // warning because of an unrecognized directive. However, we skip + // parsing them for now since we do not need these directives here (they + // are parsed and enforced in the blink CSP parser). + case CSPDirectiveName::BlockAllMixedContent: + case CSPDirectiveName::RequireTrustedTypesFor: + case CSPDirectiveName::TrustedTypes: + break; + + case CSPDirectiveName::ReportTo: + out->use_reporting_api = true; + out->report_endpoints.clear(); + ParseReportDirective(base_url, directive.second, out->use_reporting_api, + &(out->report_endpoints)); + break; + case CSPDirectiveName::ReportURI: + if (!out->use_reporting_api) + ParseReportDirective(base_url, directive.second, + out->use_reporting_api, + &(out->report_endpoints)); + break; + case CSPDirectiveName::Unknown: + out->parsing_errors.emplace_back(base::StringPrintf( + "Unrecognized Content-Security-Policy directive '%s'.", + directive.first.as_string().c_str())); + break; + } + } } } // namespace @@ -495,47 +835,35 @@ void AddContentSecurityPolicyFromHeaders( for (const auto& header : base::SplitStringPiece(header_value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { - DirectivesMap directives = ParseHeaderValue(header); auto policy = mojom::ContentSecurityPolicy::New(); + AddContentSecurityPolicyFromHeader(header, type, base_url, policy); - policy->header = mojom::ContentSecurityPolicyHeader::New( - header.as_string(), type, mojom::ContentSecurityPolicySource::kHTTP); + out->push_back(std::move(policy)); + } +} - auto frame_ancestors = directives.find("frame-ancestors"); - if (frame_ancestors != directives.end()) - ParseFrameAncestors(policy, frame_ancestors->second); +mojom::AllowCSPFromHeaderValuePtr ParseAllowCSPFromHeader( + const net::HttpResponseHeaders& headers) { + if (!base::FeatureList::IsEnabled(features::kOutOfBlinkCSPEE)) + return nullptr; - auto sandbox = directives.find("sandbox"); - if (sandbox != directives.end()) { - // Note: |ParseSandboxPolicy(...).error_message| is ignored here. Blink's - // CSP parser is already in charge of displaying it. - policy->sandbox = - ~ParseWebSandboxPolicy(sandbox->second, mojom::WebSandboxFlags::kNone) - .flags; - } + std::string allow_csp_from; + if (!headers.GetNormalizedHeader("Allow-CSP-From", &allow_csp_from)) + return nullptr; - policy->upgrade_insecure_requests |= - directives.contains("upgrade-insecure-requests"); - policy->treat_as_public_address |= - directives.contains("treat-as-public-address"); - - auto report_endpoints = directives.find("report-to"); - if (report_endpoints != directives.end()) { - if (!policy->use_reporting_api) { - policy->use_reporting_api = true; - policy->report_endpoints.clear(); - } - } else { - report_endpoints = directives.find("report-uri"); - } + base::StringPiece trimmed = + base::TrimWhitespaceASCII(allow_csp_from, base::TRIM_ALL); - if (report_endpoints != directives.end()) { - ParseReportEndpoint(policy, base_url, report_endpoints->second, - policy->use_reporting_api); - } + if (trimmed == "*") + return mojom::AllowCSPFromHeaderValue::NewAllowStar(true); - out->push_back(std::move(policy)); + GURL parsed_url = GURL(trimmed); + if (!parsed_url.is_valid()) { + return mojom::AllowCSPFromHeaderValue::NewErrorMessage( + "The 'Allow-CSP-From' header contains neither '*' nor a valid origin."); } + return mojom::AllowCSPFromHeaderValue::NewOrigin( + url::Origin::Create(parsed_url)); } bool CheckContentSecurityPolicy(const mojom::ContentSecurityPolicyPtr& policy, @@ -558,7 +886,8 @@ bool CheckContentSecurityPolicy(const mojom::ContentSecurityPolicyPtr& policy, for (CSPDirectiveName effective_directive_name = directive_name; effective_directive_name != CSPDirectiveName::Unknown; - effective_directive_name = CSPFallback(effective_directive_name)) { + effective_directive_name = + CSPFallback(effective_directive_name, directive_name)) { const auto& directive = policy->directives.find(effective_directive_name); if (directive == policy->directives.end()) continue; @@ -615,64 +944,187 @@ void UpgradeInsecureRequest(GURL* url) { *url = url->ReplaceComponents(replacements); } +bool IsValidRequiredCSPAttr( + const std::vector<mojom::ContentSecurityPolicyPtr>& policy, + const mojom::ContentSecurityPolicy* context, + std::string& error_message) { + DCHECK(policy.size() == 1); + if (!policy[0]) + return false; + + if (!policy[0]->parsing_errors.empty()) { + error_message = + "Parsing the csp attribute into a Content-Security-Policy returned one " + "or more parsing errors: " + + base::JoinString(policy[0]->parsing_errors, " "); + return false; + } + + if (!policy[0]->report_endpoints.empty()) { + error_message = + "The csp attribute cannot contain the directives 'report-to' or " + "'report-uri'."; + return false; + } + + if (context && !Subsumes(*context, policy)) { + error_message = + "The csp attribute Content-Security-Policy is not subsumed by the " + "frame's parent csp attribute Content-Security-Policy."; + return false; + } + + return true; +} + +bool Subsumes(const mojom::ContentSecurityPolicy& policy_a, + const std::vector<mojom::ContentSecurityPolicyPtr>& policies_b) { + if (policy_a.directives.empty()) + return true; + + if (policy_a.header->type == mojom::ContentSecurityPolicyType::kReport) + return true; + + // TODO(antoniosartori): Complete the implementation of this function + return util::ranges::all_of( + policy_a.directives, [&policies_b](const auto& directive_a) { + return util::ranges::any_of( + policies_b, [&directive_a](const auto& policy_b) { + if (policy_b->header->type == + mojom::ContentSecurityPolicyType::kReport) { + return false; + } + + auto value_b = policy_b->directives.find(directive_a.first); + return value_b != policy_b->directives.end() && + value_b->second == directive_a.second; + }); + }); +} + CSPDirectiveName ToCSPDirectiveName(const std::string& name) { - if (name == "default-src") - return CSPDirectiveName::DefaultSrc; + if (name == "base-uri") + return CSPDirectiveName::BaseURI; + if (name == "block-all-mixed-content") + return CSPDirectiveName::BlockAllMixedContent; if (name == "child-src") return CSPDirectiveName::ChildSrc; + if (name == "connect-src") + return CSPDirectiveName::ConnectSrc; + if (name == "default-src") + return CSPDirectiveName::DefaultSrc; + if (name == "frame-ancestors") + return CSPDirectiveName::FrameAncestors; if (name == "frame-src") return CSPDirectiveName::FrameSrc; + if (name == "font-src") + return CSPDirectiveName::FontSrc; if (name == "form-action") return CSPDirectiveName::FormAction; - if (name == "navigate-to") - return CSPDirectiveName::NavigateTo; - if (name == "frame-ancestors") - return CSPDirectiveName::FrameAncestors; if (name == "img-src") return CSPDirectiveName::ImgSrc; + if (name == "manifest-src") + return CSPDirectiveName::ManifestSrc; if (name == "media-src") return CSPDirectiveName::MediaSrc; if (name == "object-src") return CSPDirectiveName::ObjectSrc; + if (name == "prefetch-src") + return CSPDirectiveName::PrefetchSrc; + if (name == "report-uri") + return CSPDirectiveName::ReportURI; + if (name == "require-trusted-types-for") + return CSPDirectiveName::RequireTrustedTypesFor; + if (name == "sandbox") + return CSPDirectiveName::Sandbox; if (name == "script-src") return CSPDirectiveName::ScriptSrc; + if (name == "script-src-attr") + return CSPDirectiveName::ScriptSrcAttr; + if (name == "script-src-elem") + return CSPDirectiveName::ScriptSrcElem; if (name == "style-src") return CSPDirectiveName::StyleSrc; + if (name == "style-src-attr") + return CSPDirectiveName::StyleSrcAttr; + if (name == "style-src-elem") + return CSPDirectiveName::StyleSrcElem; + if (name == "treat-as-public-address") + return CSPDirectiveName::TreatAsPublicAddress; + if (name == "trusted-types") + return CSPDirectiveName::TrustedTypes; + if (name == "upgrade-insecure-requests") + return CSPDirectiveName::UpgradeInsecureRequests; if (name == "worker-src") return CSPDirectiveName::WorkerSrc; - if (name == "connect-src") - return CSPDirectiveName::ConnectSrc; + if (name == "report-to") + return CSPDirectiveName::ReportTo; + if (name == "navigate-to") + return CSPDirectiveName::NavigateTo; + return CSPDirectiveName::Unknown; } std::string ToString(CSPDirectiveName name) { switch (name) { - case CSPDirectiveName::DefaultSrc: - return "default-src"; + case CSPDirectiveName::BaseURI: + return "base-uri"; + case CSPDirectiveName::BlockAllMixedContent: + return "block-all-mixed-content"; case CSPDirectiveName::ChildSrc: return "child-src"; + case CSPDirectiveName::ConnectSrc: + return "connect-src"; + case CSPDirectiveName::DefaultSrc: + return "default-src"; + case CSPDirectiveName::FrameAncestors: + return "frame-ancestors"; case CSPDirectiveName::FrameSrc: return "frame-src"; + case CSPDirectiveName::FontSrc: + return "font-src"; case CSPDirectiveName::FormAction: return "form-action"; - case CSPDirectiveName::NavigateTo: - return "navigate-to"; - case CSPDirectiveName::FrameAncestors: - return "frame-ancestors"; case CSPDirectiveName::ImgSrc: return "img-src"; + case CSPDirectiveName::ManifestSrc: + return "manifest-src"; case CSPDirectiveName::MediaSrc: return "media-src"; case CSPDirectiveName::ObjectSrc: return "object-src"; + case CSPDirectiveName::PrefetchSrc: + return "prefetch-src"; + case CSPDirectiveName::ReportURI: + return "report-uri"; + case CSPDirectiveName::RequireTrustedTypesFor: + return "require-trusted-types-for"; + case CSPDirectiveName::Sandbox: + return "sandbox"; case CSPDirectiveName::ScriptSrc: return "script-src"; + case CSPDirectiveName::ScriptSrcAttr: + return "script-src-attr"; + case CSPDirectiveName::ScriptSrcElem: + return "script-src-elem"; case CSPDirectiveName::StyleSrc: return "style-src"; + case CSPDirectiveName::StyleSrcAttr: + return "style-src-attr"; + case CSPDirectiveName::StyleSrcElem: + return "style-src-elem"; + case CSPDirectiveName::UpgradeInsecureRequests: + return "upgrade-insecure-requests"; + case CSPDirectiveName::TreatAsPublicAddress: + return "treat-as-public-address"; + case CSPDirectiveName::TrustedTypes: + return "trusted-types"; case CSPDirectiveName::WorkerSrc: return "worker-src"; - case CSPDirectiveName::ConnectSrc: - return "connect-src"; + case CSPDirectiveName::ReportTo: + return "report-to"; + case CSPDirectiveName::NavigateTo: + return "navigate-to"; case CSPDirectiveName::Unknown: return ""; } diff --git a/chromium/services/network/public/cpp/content_security_policy/content_security_policy.h b/chromium/services/network/public/cpp/content_security_policy/content_security_policy.h index 82185a88df2..80b967e3b00 100644 --- a/chromium/services/network/public/cpp/content_security_policy/content_security_policy.h +++ b/chromium/services/network/public/cpp/content_security_policy/content_security_policy.h @@ -36,6 +36,11 @@ void AddContentSecurityPolicyFromHeaders( const GURL& base_url, std::vector<mojom::ContentSecurityPolicyPtr>* out); +// Parse and return the Allow-CSP-From header value from |headers|. +COMPONENT_EXPORT(NETWORK_CPP) +mojom::AllowCSPFromHeaderValuePtr ParseAllowCSPFromHeader( + const net::HttpResponseHeaders& headers); + // Return true when the |policy| allows a request to the |url| in relation to // the |directive| for a given |context|. // Note: Any policy violation are reported to the |context|. @@ -66,6 +71,22 @@ bool ShouldTreatAsPublicAddress( COMPONENT_EXPORT(NETWORK_CPP) void UpgradeInsecureRequest(GURL* url); +// Checks whether |policy| is a valid required CSP attribute according to +// https://w3c.github.io/webappsec-cspee/#iframe-csp-valid-attribute-value. +// |policy| must be a vector containing exactly one entry. +// The context can be null. +COMPONENT_EXPORT(NETWORK_CPP) +bool IsValidRequiredCSPAttr( + const std::vector<mojom::ContentSecurityPolicyPtr>& policy, + const mojom::ContentSecurityPolicy* context, + std::string& error_message); + +// Checks whether |policy_a| subsumes the policy list |policies_b| according to +// the algorithm https://w3c.github.io/webappsec-cspee/#subsume-policy-list. +COMPONENT_EXPORT(NETWORK_CPP) +bool Subsumes(const mojom::ContentSecurityPolicy& policy_a, + const std::vector<mojom::ContentSecurityPolicyPtr>& policies_b); + COMPONENT_EXPORT(NETWORK_CPP) mojom::CSPDirectiveName ToCSPDirectiveName(const std::string& name); diff --git a/chromium/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc b/chromium/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc index 6ac9b0d0d1b..c4ca0e20494 100644 --- a/chromium/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc +++ b/chromium/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc @@ -4,7 +4,6 @@ #include "services/network/public/cpp/content_security_policy/content_security_policy.h" -#include "base/optional.h" #include "base/stl_util.h" #include "net/http/http_response_headers.h" #include "services/network/public/cpp/content_security_policy/csp_context.h" @@ -234,17 +233,19 @@ TEST(ContentSecurityPolicy, ParseFrameAncestors) { TestFrameAncestorsCSPParser(test.header, &test.expected_result); } -TEST(ContentSecurityPolicy, ParseMultipleDirectives) { - // First directive is valid, second one is ignored. +TEST(ContentSecurityPolicy, ParseDirectives) { + // One duplicate directive. { scoped_refptr<net::HttpResponseHeaders> headers( new net::HttpResponseHeaders("HTTP/1.1 200 OK")); headers->SetHeader("Content-Security-Policy", - "frame-ancestors example.com; other_directive " - "value; frame-ancestors example.org"); + "frame-ancestors example.com; script-src " + "example2.com; frame-ancestors example3.com"); std::vector<mojom::ContentSecurityPolicyPtr> policies; AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), &policies); + EXPECT_EQ(2U, policies[0]->directives.size()); + auto& frame_ancestors = policies[0]->directives[mojom::CSPDirectiveName::FrameAncestors]; EXPECT_EQ(frame_ancestors->sources.size(), 1U); @@ -255,18 +256,37 @@ TEST(ContentSecurityPolicy, ParseMultipleDirectives) { EXPECT_EQ(frame_ancestors->sources[0]->is_host_wildcard, false); EXPECT_EQ(frame_ancestors->sources[0]->is_port_wildcard, false); EXPECT_EQ(frame_ancestors->allow_self, false); + + auto& script_src = + policies[0]->directives[mojom::CSPDirectiveName::ScriptSrc]; + EXPECT_EQ(script_src->sources.size(), 1U); + EXPECT_EQ(script_src->sources[0]->scheme, ""); + EXPECT_EQ(script_src->sources[0]->host, "example2.com"); + EXPECT_EQ(script_src->sources[0]->port, url::PORT_UNSPECIFIED); + EXPECT_EQ(script_src->sources[0]->path, ""); + EXPECT_EQ(script_src->sources[0]->is_host_wildcard, false); + EXPECT_EQ(script_src->sources[0]->is_port_wildcard, false); + EXPECT_EQ(script_src->allow_self, false); + + EXPECT_EQ(1U, policies[0]->parsing_errors.size()); + EXPECT_EQ( + "Ignoring duplicate Content-Security-Policy directive " + "'frame-ancestors'.", + policies[0]->parsing_errors[0]); } - // Skip the first directive, which is not frame-ancestors. + // One invalid directive. { scoped_refptr<net::HttpResponseHeaders> headers( new net::HttpResponseHeaders("HTTP/1.1 200 OK")); headers->SetHeader("Content-Security-Policy", - "other_directive value; frame-ancestors " + "other-directive value; frame-ancestors " "example.org"); std::vector<mojom::ContentSecurityPolicyPtr> policies; AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), &policies); + EXPECT_EQ(1U, policies[0]->directives.size()); + auto& frame_ancestors = policies[0]->directives[mojom::CSPDirectiveName::FrameAncestors]; EXPECT_EQ(frame_ancestors->sources.size(), 1U); @@ -278,6 +298,143 @@ TEST(ContentSecurityPolicy, ParseMultipleDirectives) { EXPECT_EQ(frame_ancestors->sources[0]->is_port_wildcard, false); EXPECT_EQ(frame_ancestors->allow_self, false); EXPECT_EQ(frame_ancestors->allow_star, false); + + EXPECT_EQ(1U, policies[0]->parsing_errors.size()); + EXPECT_EQ( + "Unrecognized Content-Security-Policy directive 'other-directive'.", + policies[0]->parsing_errors[0]); + } + + // Invalid characters in directive name. + { + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders("HTTP/1.1 200 OK")); + headers->SetHeader("Content-Security-Policy", + "frame_ancestors example.com;"); + std::vector<mojom::ContentSecurityPolicyPtr> policies; + AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), + &policies); + EXPECT_TRUE(policies[0]->directives.empty()); + + EXPECT_EQ(1U, policies[0]->parsing_errors.size()); + EXPECT_EQ( + "The Content-Security-Policy directive name 'frame_ancestors' contains " + "one or more invalid characters. Only ASCII alphanumeric characters or " + "dashes '-' are allowed in directive names.", + policies[0]->parsing_errors[0]); + } + + // Invalid characters in directive value. + { + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders("HTTP/1.1 200 OK")); + headers->SetHeader("Content-Security-Policy", "frame-ancestors ü.com;"); + std::vector<mojom::ContentSecurityPolicyPtr> policies; + AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), + &policies); + EXPECT_TRUE(policies[0]->directives.empty()); + + EXPECT_EQ(1U, policies[0]->parsing_errors.size()); + EXPECT_EQ( + "The value for the Content-Security-Policy directive 'frame-ancestors' " + "contains one or more invalid characters. Non-whitespace characters " + "outside ASCII 0x21-0x7E must be percent-encoded, as described in RFC " + "3986, section 2.1: http://tools.ietf.org/html/rfc3986#section-2.1.", + policies[0]->parsing_errors[0]); + } + + // Missing semicolon between directive names. + { + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders("HTTP/1.1 200 OK")); + headers->SetHeader("Content-Security-Policy", "frame-ancestors object-src"); + std::vector<mojom::ContentSecurityPolicyPtr> policies; + AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), + &policies); + EXPECT_EQ(1U, policies[0]->directives.size()); + + auto& frame_ancestors = + policies[0]->directives[mojom::CSPDirectiveName::FrameAncestors]; + EXPECT_EQ(frame_ancestors->sources.size(), 1U); + EXPECT_EQ(frame_ancestors->sources[0]->scheme, ""); + EXPECT_EQ(frame_ancestors->sources[0]->host, "object-src"); + EXPECT_EQ(frame_ancestors->sources[0]->port, url::PORT_UNSPECIFIED); + EXPECT_EQ(frame_ancestors->sources[0]->path, ""); + EXPECT_EQ(frame_ancestors->sources[0]->is_host_wildcard, false); + EXPECT_EQ(frame_ancestors->sources[0]->is_port_wildcard, false); + EXPECT_EQ(frame_ancestors->allow_self, false); + EXPECT_EQ(frame_ancestors->allow_star, false); + + EXPECT_EQ(1U, policies[0]->parsing_errors.size()); + EXPECT_EQ( + "The Content-Security-Policy directive 'frame-ancestors' contains " + "'object-src' as a source expression. Did you want to add it as a " + "directive and forget a semicolon?", + policies[0]->parsing_errors[0]); + } + + // Path containing query. + { + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders("HTTP/1.1 200 OK")); + headers->SetHeader("Content-Security-Policy", + "frame-ancestors http://example.org/index.html?a=b;"); + std::vector<mojom::ContentSecurityPolicyPtr> policies; + AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), + &policies); + EXPECT_EQ(1U, policies[0]->directives.size()); + + auto& frame_ancestors = + policies[0]->directives[mojom::CSPDirectiveName::FrameAncestors]; + EXPECT_EQ(frame_ancestors->sources.size(), 1U); + EXPECT_EQ(frame_ancestors->sources[0]->scheme, "http"); + EXPECT_EQ(frame_ancestors->sources[0]->host, "example.org"); + EXPECT_EQ(frame_ancestors->sources[0]->port, url::PORT_UNSPECIFIED); + EXPECT_EQ(frame_ancestors->sources[0]->path, "/index.html"); + EXPECT_EQ(frame_ancestors->sources[0]->is_host_wildcard, false); + EXPECT_EQ(frame_ancestors->sources[0]->is_port_wildcard, false); + EXPECT_EQ(frame_ancestors->allow_self, false); + EXPECT_EQ(frame_ancestors->allow_star, false); + + EXPECT_EQ(1U, policies[0]->parsing_errors.size()); + EXPECT_EQ( + "The source list for Content-Security-Policy directive " + "'frame-ancestors' contains a source with an invalid path: " + "'/index.html?a=b'. The query component, including the '?', will be " + "ignored.", + policies[0]->parsing_errors[0]); + } + + // Path containing ref. + { + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders("HTTP/1.1 200 OK")); + headers->SetHeader("Content-Security-Policy", + "frame-ancestors http://example.org/index.html#a;"); + std::vector<mojom::ContentSecurityPolicyPtr> policies; + AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), + &policies); + EXPECT_EQ(1U, policies[0]->directives.size()); + + auto& frame_ancestors = + policies[0]->directives[mojom::CSPDirectiveName::FrameAncestors]; + EXPECT_EQ(frame_ancestors->sources.size(), 1U); + EXPECT_EQ(frame_ancestors->sources[0]->scheme, "http"); + EXPECT_EQ(frame_ancestors->sources[0]->host, "example.org"); + EXPECT_EQ(frame_ancestors->sources[0]->port, url::PORT_UNSPECIFIED); + EXPECT_EQ(frame_ancestors->sources[0]->path, "/index.html"); + EXPECT_EQ(frame_ancestors->sources[0]->is_host_wildcard, false); + EXPECT_EQ(frame_ancestors->sources[0]->is_port_wildcard, false); + EXPECT_EQ(frame_ancestors->allow_self, false); + EXPECT_EQ(frame_ancestors->allow_star, false); + + EXPECT_EQ(1U, policies[0]->parsing_errors.size()); + EXPECT_EQ( + "The source list for Content-Security-Policy directive " + "'frame-ancestors' contains a source with an invalid path: " + "'/index.html#a'. The fragment identifier, including the '#', will be " + "ignored.", + policies[0]->parsing_errors[0]); } // Multiple CSP headers with multiple frame-ancestors directives present. @@ -324,7 +481,7 @@ TEST(ContentSecurityPolicy, ParseMultipleDirectives) { scoped_refptr<net::HttpResponseHeaders> headers( new net::HttpResponseHeaders("HTTP/1.1 200 OK")); headers->SetHeader("Content-Security-Policy", - "other_directive value, frame-ancestors example.org"); + "other-directive value, frame-ancestors example.org"); std::vector<mojom::ContentSecurityPolicyPtr> policies; AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), &policies); @@ -557,7 +714,9 @@ TEST(ContentSecurityPolicy, DirectiveFallback) { auto allow_host = [](const char* host) { std::vector<mojom::CSPSourcePtr> sources; sources.push_back(BuildCSPSource("http", host)); - return mojom::CSPSourceList::New(std::move(sources), false, false, false); + auto csp_source_list = mojom::CSPSourceList::New(); + csp_source_list->sources = std::move(sources); + return csp_source_list; }; { @@ -702,17 +861,16 @@ TEST(ContentSecurityPolicy, NavigateToChecks) { GURL url_a("https://a"); GURL url_b("https://b"); CSPContextTest context; - auto allow_none = [] { - return mojom::CSPSourceList::New(std::vector<mojom::CSPSourcePtr>(), false, - false, false); - }; + auto allow_none = [] { return mojom::CSPSourceList::New(); }; auto allow_self = [] { - return mojom::CSPSourceList::New(std::vector<mojom::CSPSourcePtr>(), true, - false, false); + auto csp = mojom::CSPSourceList::New(); + csp->allow_self = true; + return csp; }; auto allow_redirect = [] { - return mojom::CSPSourceList::New(std::vector<mojom::CSPSourcePtr>(), false, - false, true); + auto csp = mojom::CSPSourceList::New(); + csp->allow_response_redirects = true; + return csp; }; auto source_a = [] { return mojom::CSPSource::New("https", "a", url::PORT_UNSPECIFIED, "", false, @@ -721,12 +879,17 @@ TEST(ContentSecurityPolicy, NavigateToChecks) { auto allow_a = [&] { std::vector<mojom::CSPSourcePtr> sources; sources.push_back(source_a()); - return mojom::CSPSourceList::New(std::move(sources), false, false, false); + auto csp = mojom::CSPSourceList::New(); + csp->sources = std::move(sources); + return csp; }; auto allow_redirect_a = [&] { std::vector<mojom::CSPSourcePtr> sources; sources.push_back(source_a()); - return mojom::CSPSourceList::New(std::move(sources), false, false, true); + auto csp = mojom::CSPSourceList::New(); + csp->sources = std::move(sources); + csp->allow_response_redirects = true; + return csp; }; context.SetSelf(source_a()); @@ -800,4 +963,249 @@ TEST(ContentSecurityPolicy, ParseSandbox) { mojom::WebSandboxFlags::kAutomaticFeatures); } +TEST(ContentSecurityPolicy, ParseSerializedSourceList) { + struct TestCase { + std::string directive_value; + base::Callback<mojom::CSPSourceListPtr()> expected; + std::string expected_error; + } cases[] = { + { + "'nonce-a' 'nonce-a=' 'nonce-a==' 'nonce-a===' 'nonce-==' 'nonce-' " + "'nonce 'nonce-cde' 'nonce-cde=' 'nonce-cde==' 'nonce-cde==='", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->nonces.push_back("a"); + csp->nonces.push_back("a="); + csp->nonces.push_back("a=="); + csp->nonces.push_back("cde"); + csp->nonces.push_back("cde="); + csp->nonces.push_back("cde=="); + return csp; + }), + "", + }, + { + "'sha256-abc' 'sha256-ABC' 'sha256 'sha256-' 'sha384-abc' " + "'sha512-abc' 'sha-abc' 'sha256-*' 'sha-256-cde' 'sha-384-cde' " + "'sha-512-cde'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->hashes.push_back(mojom::CSPHashSource::New( + mojom::CSPHashAlgorithm::SHA256, "abc")); + csp->hashes.push_back(mojom::CSPHashSource::New( + mojom::CSPHashAlgorithm::SHA256, "ABC")); + csp->hashes.push_back(mojom::CSPHashSource::New( + mojom::CSPHashAlgorithm::SHA384, "abc")); + csp->hashes.push_back(mojom::CSPHashSource::New( + mojom::CSPHashAlgorithm::SHA512, "abc")); + csp->hashes.push_back(mojom::CSPHashSource::New( + mojom::CSPHashAlgorithm::SHA256, "cde")); + csp->hashes.push_back(mojom::CSPHashSource::New( + mojom::CSPHashAlgorithm::SHA384, "cde")); + csp->hashes.push_back(mojom::CSPHashSource::New( + mojom::CSPHashAlgorithm::SHA512, "cde")); + return csp; + }), + "", + }, + { + "'none' ", + base::Bind([] { return mojom::CSPSourceList::New(); }), + "", + }, + { + "'none' 'self'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->allow_self = true; + return csp; + }), + "The Content-Security-Policy directive 'script-src' contains the " + "keyword 'none' alongside with other source expressions. The keyword " + "'none' must be the only source expression in the directive value, " + "otherwise it is ignored.", + }, + { + "'self' 'none'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->allow_self = true; + return csp; + }), + "The Content-Security-Policy directive 'script-src' contains the " + "keyword 'none' alongside with other source expressions. The keyword " + "'none' must be the only source expression in the directive value, " + "otherwise it is ignored.", + }, + { + "'self'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->allow_self = true; + return csp; + }), + }, + { + "'wrong' *", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->allow_star = true; + return csp; + }), + "The source list for the Content-Security-Policy directive " + "'script-src' contains an invalid source: ''wrong''.", + }, + { + "'wrong' 'unsafe-inline'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->allow_inline = true; + return csp; + }), + "The source list for the Content-Security-Policy directive " + "'script-src' contains an invalid source: ''wrong''.", + }, + { + "'wrong' 'unsafe-eval'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->allow_eval = true; + return csp; + }), + "The source list for the Content-Security-Policy directive " + "'script-src' contains an invalid source: ''wrong''.", + }, + { + "'wrong' 'wasm-eval'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->allow_wasm_eval = true; + return csp; + }), + "The source list for the Content-Security-Policy directive " + "'script-src' contains an invalid source: ''wrong''.", + }, + { + "'wrong' 'strict-dynamic'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->allow_dynamic = true; + return csp; + }), + "The source list for the Content-Security-Policy directive " + "'script-src' contains an invalid source: ''wrong''.", + }, + { + "'wrong' 'unsafe-hashes'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->allow_unsafe_hashes = true; + return csp; + }), + "The source list for the Content-Security-Policy directive " + "'script-src' contains an invalid source: ''wrong''.", + }, + { + "'wrong' 'report-sample'", + base::Bind([] { + auto csp = mojom::CSPSourceList::New(); + csp->report_sample = true; + return csp; + }), + "The source list for the Content-Security-Policy directive " + "'script-src' contains an invalid source: ''wrong''.", + }, + }; + + for (auto& test : cases) { + SCOPED_TRACE(test.directive_value); + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders("HTTP/1.1 200 OK")); + headers->SetHeader("Content-Security-Policy", + "script-src " + test.directive_value); + std::vector<mojom::ContentSecurityPolicyPtr> policies; + AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), + &policies); + EXPECT_TRUE(test.expected.Run().Equals( + policies[0]->directives[mojom::CSPDirectiveName::ScriptSrc])); + + if (!test.expected_error.empty()) + EXPECT_EQ(test.expected_error, policies[0]->parsing_errors[0]); + } +} + +TEST(ContentSecurityPolicy, IsValidRequiredCSPAttr) { + struct TestCase { + const char* csp; + bool expected; + std::string expected_error; + } cases[] = {{"script-src 'none'", true, ""}, + {"script-src 'none'; invalid-directive", false, + "Parsing the csp attribute into a Content-Security-Policy " + "returned one or more parsing errors: Unrecognized " + "Content-Security-Policy directive 'invalid-directive'."}, + {"script-src 'none'; report-uri https://www.example.com", false, + "The csp attribute cannot contain the directives 'report-to' " + "or 'report-uri'."}}; + + for (auto& test : cases) { + SCOPED_TRACE(test.csp); + std::vector<mojom::ContentSecurityPolicyPtr> csp; + auto required_csp_headers = + base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK"); + required_csp_headers->SetHeader("Content-Security-Policy", test.csp); + AddContentSecurityPolicyFromHeaders(*required_csp_headers, + GURL("https://example.com/"), &csp); + std::string out; + EXPECT_EQ(test.expected, IsValidRequiredCSPAttr(csp, nullptr, out)); + EXPECT_EQ(test.expected_error, out); + } +} + +TEST(ContentSecurityPolicy, Subsumes) { + struct TestCase { + std::string name; + std::string required_csp; + std::string returned_csp; + bool expected; + } cases[] = { + { + "No required csp", + "", + "script-src 'none'", + true, + }, + { + "Same CSPs", + "script-src 'none'", + "script-src 'none'", + true, + }, + }; + + for (auto& test : cases) { + SCOPED_TRACE(test.name); + std::vector<mojom::ContentSecurityPolicyPtr> required_csp; + if (test.required_csp.empty()) { + required_csp.push_back(mojom::ContentSecurityPolicy::New()); + } else { + auto required_csp_headers = + base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK"); + required_csp_headers->SetHeader("Content-Security-Policy", + test.required_csp); + AddContentSecurityPolicyFromHeaders( + *required_csp_headers, GURL("https://example.com/"), &required_csp); + } + + auto returned_csp_headers = + base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK"); + returned_csp_headers->AddHeader("Content-Security-Policy", + test.returned_csp); + std::vector<mojom::ContentSecurityPolicyPtr> returned_csp; + AddContentSecurityPolicyFromHeaders( + *returned_csp_headers, GURL("https://example.com/"), &returned_csp); + EXPECT_EQ(test.expected, Subsumes(*required_csp[0], returned_csp)); + } +} + } // namespace network diff --git a/chromium/services/network/public/cpp/content_security_policy/csp_source.cc b/chromium/services/network/public/cpp/content_security_policy/csp_source.cc index 1e029582b11..983b8a51642 100644 --- a/chromium/services/network/public/cpp/content_security_policy/csp_source.cc +++ b/chromium/services/network/public/cpp/content_security_policy/csp_source.cc @@ -48,6 +48,17 @@ enum class PortMatchingResult { }; enum class SchemeMatchingResult { NotMatching, MatchingUpgrade, MatchingExact }; +SchemeMatchingResult MatchScheme(const std::string& scheme_a, + const std::string& scheme_b) { + if (scheme_a == scheme_b) + return SchemeMatchingResult::MatchingExact; + if ((scheme_a == url::kHttpScheme && scheme_b == url::kHttpsScheme) || + (scheme_a == url::kWsScheme && scheme_b == url::kWssScheme)) { + return SchemeMatchingResult::MatchingUpgrade; + } + return SchemeMatchingResult::NotMatching; +} + SchemeMatchingResult SourceAllowScheme(const mojom::CSPSourcePtr& source, const GURL& url, CSPContext* context) { @@ -60,19 +71,11 @@ SchemeMatchingResult SourceAllowScheme(const mojom::CSPSourcePtr& source, const std::string& allowed_scheme = source->scheme.empty() ? context->self_source()->scheme : source->scheme; - if (url.SchemeIs(allowed_scheme)) - return SchemeMatchingResult::MatchingExact; - - // Implicitly allow using a more secure version of a protocol when the - // non-secure one is allowed. - if ((allowed_scheme == url::kHttpScheme && url.SchemeIs(url::kHttpsScheme)) || - (allowed_scheme == url::kWsScheme && url.SchemeIs(url::kWssScheme))) { - return SchemeMatchingResult::MatchingUpgrade; - } - return SchemeMatchingResult::NotMatching; + return MatchScheme(allowed_scheme, url.scheme()); } -bool SourceAllowHost(const mojom::CSPSourcePtr& source, const GURL& url) { +bool SourceAllowHost(const mojom::CSPSourcePtr& source, + const std::string& host) { if (source->is_host_wildcard) { if (source->host.empty()) return true; @@ -80,66 +83,86 @@ bool SourceAllowHost(const mojom::CSPSourcePtr& source, const GURL& url) { // The renderer version of this function counts how many times it happens. // It might be useful to do it outside of blink too. // See third_party/blink/renderer/core/frame/csp/csp_source.cc - return base::EndsWith(url.host(), '.' + source->host, + return base::EndsWith(host, '.' + source->host, base::CompareCase::INSENSITIVE_ASCII); } else { - return base::EqualsCaseInsensitiveASCII(url.host(), source->host); + return base::EqualsCaseInsensitiveASCII(host, source->host); } } -PortMatchingResult SourceAllowPort(const mojom::CSPSourcePtr& source, - const GURL& url) { - int url_port = url.EffectiveIntPort(); +bool SourceAllowHost(const mojom::CSPSourcePtr& source, const GURL& url) { + return SourceAllowHost(source, url.host()); +} +PortMatchingResult SourceAllowPort(const mojom::CSPSourcePtr& source, + int port, + const std::string& scheme) { if (source->is_port_wildcard) return PortMatchingResult::MatchingWildcard; - if (source->port == url_port) { + if (source->port == port) { if (source->port == url::PORT_UNSPECIFIED) return PortMatchingResult::MatchingWildcard; return PortMatchingResult::MatchingExact; } if (source->port == url::PORT_UNSPECIFIED) { - if (DefaultPortForScheme(url.scheme()) == url_port) { + if (DefaultPortForScheme(scheme) == port) + return PortMatchingResult::MatchingWildcard; + } + + if (port == url::PORT_UNSPECIFIED) { + if (source->port == DefaultPortForScheme(scheme)) return PortMatchingResult::MatchingWildcard; - } - return PortMatchingResult::NotMatching; } int source_port = source->port; if (source_port == url::PORT_UNSPECIFIED) source_port = DefaultPortForScheme(source->scheme); - if (source_port == 80 && url_port == 443) + if (port == url::PORT_UNSPECIFIED) + port = DefaultPortForScheme(scheme); + + if (source_port == 80 && port == 443) return PortMatchingResult::MatchingUpgrade; return PortMatchingResult::NotMatching; } -bool SourceAllowPath(const mojom::CSPSourcePtr& source, - const GURL& url, - bool has_followed_redirect) { - if (has_followed_redirect) - return true; - - if (source->path.empty() || url.path().empty()) - return true; +PortMatchingResult SourceAllowPort(const mojom::CSPSourcePtr& source, + const GURL& url) { + return SourceAllowPort(source, url.EffectiveIntPort(), url.scheme()); +} - std::string url_path; - if (!DecodePath(url.path(), &url_path)) { +bool SourceAllowPath(const mojom::CSPSourcePtr& source, + const std::string& path) { + std::string path_decoded; + if (!DecodePath(path, &path_decoded)) { // TODO(arthursonzogni): try to figure out if that could happen and how to // handle it. return false; } + if (source->path.empty() || (source->path == "/" && path_decoded.empty())) + return true; + // If the path represents a directory. - if (base::EndsWith(source->path, "/", base::CompareCase::SENSITIVE)) - return base::StartsWith(url_path, source->path, + if (base::EndsWith(source->path, "/", base::CompareCase::SENSITIVE)) { + return base::StartsWith(path_decoded, source->path, base::CompareCase::SENSITIVE); + } // The path represents a file. - return source->path == url_path; + return source->path == path_decoded; +} + +bool SourceAllowPath(const mojom::CSPSourcePtr& source, + const GURL& url, + bool has_followed_redirect) { + if (has_followed_redirect) + return true; + + return SourceAllowPath(source, url.path()); } bool requiresUpgrade(const PortMatchingResult result) { @@ -181,6 +204,42 @@ bool CheckCSPSource(const mojom::CSPSourcePtr& source, SourceAllowPath(source, url, has_followed_redirect); } +// Check whether |source_a| subsumes |source_b|. +bool CSPSourceSubsumes(const mojom::CSPSourcePtr& source_a, + const mojom::CSPSourcePtr& source_b) { + // If the original source expressions didn't have a scheme, we should have + // filled that already with origin's scheme. + DCHECK(!source_a->scheme.empty()); + DCHECK(!source_b->scheme.empty()); + + if (MatchScheme(source_a->scheme, source_b->scheme) == + SchemeMatchingResult::NotMatching) { + return false; + } + + if (IsSchemeOnly(source_a)) + return true; + if (IsSchemeOnly(source_b)) + return false; + + if (!SourceAllowHost(source_a, (source_b->is_host_wildcard ? "*." : "") + + source_b->host)) { + return false; + } + + if (source_b->is_port_wildcard && !source_a->is_port_wildcard) + return false; + PortMatchingResult port_matching = + SourceAllowPort(source_a, source_b->port, source_b->scheme); + if (port_matching == PortMatchingResult::NotMatching) + return false; + + if (!SourceAllowPath(source_a, source_b->path)) + return false; + + return true; +} + std::string ToString(const mojom::CSPSourcePtr& source) { // scheme if (IsSchemeOnly(source)) diff --git a/chromium/services/network/public/cpp/content_security_policy/csp_source.h b/chromium/services/network/public/cpp/content_security_policy/csp_source.h index 902aed3eb93..d0e6c05fa5a 100644 --- a/chromium/services/network/public/cpp/content_security_policy/csp_source.h +++ b/chromium/services/network/public/cpp/content_security_policy/csp_source.h @@ -22,6 +22,12 @@ bool CheckCSPSource(const mojom::CSPSourcePtr& source, CSPContext* context, bool has_followed_redirect = false); +// Check if |source_a| subsumes |source_b| according to +// https://w3c.github.io/webappsec-cspee/#subsume-source-expressions +COMPONENT_EXPORT(NETWORK_CPP) +bool CSPSourceSubsumes(const mojom::CSPSourcePtr& source_a, + const mojom::CSPSourcePtr& source_b); + // Serialize the CSPSource |source| as a string. This is used for reporting // violations. COMPONENT_EXPORT(NETWORK_CPP) diff --git a/chromium/services/network/public/cpp/content_security_policy/csp_source_list_unittest.cc b/chromium/services/network/public/cpp/content_security_policy/csp_source_list_unittest.cc index 800919d0148..8b709edc6a4 100644 --- a/chromium/services/network/public/cpp/content_security_policy/csp_source_list_unittest.cc +++ b/chromium/services/network/public/cpp/content_security_policy/csp_source_list_unittest.cc @@ -34,8 +34,8 @@ TEST(CSPSourceList, MultipleSource) { "", false, false)); sources.push_back(mojom::CSPSource::New("", "b.com", url::PORT_UNSPECIFIED, "", false, false)); - auto source_list = - mojom::CSPSourceList::New(std::move(sources), false, false, false); + auto source_list = mojom::CSPSourceList::New(); + source_list->sources = std::move(sources); EXPECT_TRUE(Allow(source_list, GURL("http://a.com"), &context)); EXPECT_TRUE(Allow(source_list, GURL("http://b.com"), &context)); EXPECT_FALSE(Allow(source_list, GURL("http://c.com"), &context)); @@ -44,11 +44,8 @@ TEST(CSPSourceList, MultipleSource) { TEST(CSPSourceList, AllowStar) { CSPContext context; context.SetSelf(url::Origin::Create(GURL("http://example.com"))); - auto source_list = mojom::CSPSourceList::New( - std::vector<mojom::CSPSourcePtr>(), // source_list - false, // allow_self - true, // allow_star - false); // allow_redirects + auto source_list = mojom::CSPSourceList::New(); + source_list->allow_star = true; EXPECT_TRUE(Allow(source_list, GURL("http://not-example.com"), &context)); EXPECT_TRUE(Allow(source_list, GURL("https://not-example.com"), &context)); EXPECT_TRUE(Allow(source_list, GURL("ws://not-example.com"), &context)); @@ -67,11 +64,8 @@ TEST(CSPSourceList, AllowStar) { TEST(CSPSourceList, AllowSelf) { CSPContext context; context.SetSelf(url::Origin::Create(GURL("http://example.com"))); - auto source_list = mojom::CSPSourceList::New( - std::vector<mojom::CSPSourcePtr>(), // source_list - true, // allow_self, - false, // allow_star - false); // allow_redirects + auto source_list = mojom::CSPSourceList::New(); + source_list->allow_self = true; EXPECT_TRUE(Allow(source_list, GURL("http://example.com"), &context)); EXPECT_FALSE(Allow(source_list, GURL("http://not-example.com"), &context)); EXPECT_TRUE(Allow(source_list, GURL("https://example.com"), &context)); @@ -81,11 +75,7 @@ TEST(CSPSourceList, AllowSelf) { TEST(CSPSourceList, AllowStarAndSelf) { CSPContext context; context.SetSelf(url::Origin::Create(GURL("https://a.com"))); - auto source_list = - mojom::CSPSourceList::New(std::vector<mojom::CSPSourcePtr>(), - false, // allow_self - false, // allow_star - false); // allow_redirects + auto source_list = mojom::CSPSourceList::New(); // If the request is allowed by {*} and not by {'self'} then it should be // allowed by the union {*,'self'}. @@ -103,11 +93,8 @@ TEST(CSPSourceList, AllowStarAndSelf) { TEST(CSPSourceList, AllowSelfWithUnspecifiedPort) { CSPContext context; context.SetSelf(url::Origin::Create(GURL("https://example.com/"))); - auto source_list = mojom::CSPSourceList::New( - std::vector<mojom::CSPSourcePtr>(), // source_list - true, // allow_self - false, // allow_star - false); // allow_redirects + auto source_list = mojom::CSPSourceList::New(); + source_list->allow_self = true; EXPECT_TRUE( Allow(source_list, GURL("https://example.com/print.pdf"), &context)); @@ -116,22 +103,15 @@ TEST(CSPSourceList, AllowSelfWithUnspecifiedPort) { TEST(CSPSourceList, AllowNone) { CSPContext context; context.SetSelf(url::Origin::Create(GURL("http://example.com"))); - auto source_list = mojom::CSPSourceList::New( - std::vector<mojom::CSPSourcePtr>(), // source_list - false, // allow_self - false, // allow_star - false); // allow_redirects + auto source_list = mojom::CSPSourceList::New(); EXPECT_FALSE(Allow(source_list, GURL("http://example.com"), &context)); EXPECT_FALSE(Allow(source_list, GURL("https://example.test/"), &context)); } TEST(CSPSourceTest, SelfIsUnique) { // Policy: 'self' - auto source_list = mojom::CSPSourceList::New( - std::vector<mojom::CSPSourcePtr>(), // source_list - true, // allow_self - false, // allow_star - false); // allow_redirects + auto source_list = mojom::CSPSourceList::New(); + source_list->allow_self = true; CSPContext context; context.SetSelf(url::Origin::Create(GURL("http://a.com"))); diff --git a/chromium/services/network/public/cpp/content_security_policy/csp_source_unittest.cc b/chromium/services/network/public/cpp/content_security_policy/csp_source_unittest.cc index 903aa5a2a25..0ff85abdc45 100644 --- a/chromium/services/network/public/cpp/content_security_policy/csp_source_unittest.cc +++ b/chromium/services/network/public/cpp/content_security_policy/csp_source_unittest.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "services/network/public/cpp/content_security_policy/csp_source.h" +#include "net/http/http_response_headers.h" +#include "services/network/public/cpp/content_security_policy/content_security_policy.h" #include "services/network/public/cpp/content_security_policy/csp_context.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/origin.h" @@ -20,6 +22,17 @@ bool Allow(const network::mojom::CSPSourcePtr& source, return CheckCSPSource(source, url, context, is_redirect); } +network::mojom::CSPSourcePtr CSPSource(const std::string& raw) { + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders("HTTP/1.1 200 OK")); + headers->SetHeader("Content-Security-Policy", "script-src " + raw); + std::vector<mojom::ContentSecurityPolicyPtr> policies; + AddContentSecurityPolicyFromHeaders(*headers, GURL("https://example.com/"), + &policies); + return std::move( + policies[0]->directives[mojom::CSPDirectiveName::ScriptSrc]->sources[0]); +} + } // namespace TEST(CSPSourceTest, BasicMatching) { @@ -243,6 +256,8 @@ TEST(CSPSourceTest, AllowPath) { Allow(source, GURL("http://a.com/path/to/file/subpath"), &context)); EXPECT_FALSE( Allow(source, GURL("http://a.com/path/to/something"), &context)); + EXPECT_FALSE(Allow(source, GURL("http://a.com/"), &context)); + EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context)); } // Path to a directory @@ -254,6 +269,8 @@ TEST(CSPSourceTest, AllowPath) { EXPECT_FALSE(Allow(source, GURL("http://a.com/path/"), &context)); EXPECT_FALSE(Allow(source, GURL("http://a.com/path/to"), &context)); EXPECT_FALSE(Allow(source, GURL("http://a.com/path/to"), &context)); + EXPECT_FALSE(Allow(source, GURL("http://a.com/"), &context)); + EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context)); } // Empty path @@ -306,6 +323,184 @@ TEST(CSPSourceTest, RedirectMatching) { EXPECT_FALSE(Allow(source, GURL("http://a.com:9000/foo/"), &context, false)); } +TEST(CSPSourceTest, DoesNotSubsume) { + struct TestCase { + const char* a; + const char* b; + } cases[] = { + // In the following test cases, neither |a| subsumes |b| nor |b| subsumes + // |a|. + // Different hosts. + {"http://example.com", "http://another.com"}, + // Different schemes (wss -> http). + {"wss://example.com", "http://example.com"}, + // Different schemes (wss -> about). + {"wss://example.com/", "about://example.com/"}, + // Different schemes (wss -> about). + {"http://example.com/", "about://example.com/"}, + // Different paths. + {"http://example.com/1.html", "http://example.com/2.html"}, + // Different ports. + {"http://example.com:443/", "http://example.com:800/"}, + }; + for (const auto& test : cases) { + auto a = CSPSource(test.a); + auto b = CSPSource(test.b); + + EXPECT_FALSE(CSPSourceSubsumes(a, b)) + << test.a << " should not subsume " << test.b; + EXPECT_FALSE(CSPSourceSubsumes(b, a)) + << test.b << " should not subsume " << test.a; + } +} + +TEST(CSPSourceTest, Subsumes) { + struct TestCase { + const char* a; + const char* b; + bool expected_a_subsumes_b; + bool expected_b_subsumes_a; + } cases[] = { + // Equal signals. + {"http://a.org/", "http://a.org/", true, true}, + {"https://a.org/", "https://a.org/", true, true}, + {"https://a.org/page.html", "https://a.org/page.html", true, true}, + {"http://a.org:70", "http://a.org:70", true, true}, + {"https://a.org:70", "https://a.org:70", true, true}, + {"https://a.org/page.html", "https://a.org/page.html", true, true}, + {"http://a.org:70/page.html", "http://a.org:70/page.html", true, true}, + {"https://a.org:70/page.html", "https://a.org:70/page.html", true, true}, + {"http://a.org/", "http://a.org", true, true}, + {"http://a.org:80", "http://a.org:80", true, true}, + {"http://a.org:80", "https://a.org:443", true, false}, + {"http://a.org", "https://a.org:443", true, false}, + {"http://a.org:80", "https://a.org", true, false}, + // One stronger signal in the first CSPSource. + {"http://a.org/", "https://a.org/", true, false}, + {"http://a.org/page.html", "http://a.org/", false, true}, + {"http://a.org:80/page.html", "http://a.org:80/", false, true}, + {"http://a.org:80", "http://a.org/", true, true}, + {"http://a.org:700", "http://a.org/", false, false}, + // Two stronger signals in the first CSPSource. + {"https://a.org/page.html", "http://a.org/", false, true}, + {"https://a.org:80", "http://a.org/", false, false}, + {"http://a.org:80/page.html", "http://a.org/", false, true}, + // Three stronger signals in the first CSPSource. + {"https://a.org:70/page.html", "http://a.org/", false, false}, + // Mixed signals. + {"https://a.org/", "http://a.org/page.html", false, false}, + {"https://a.org", "http://a.org:70/", false, false}, + {"http://a.org/page.html", "http://a.org:70/", false, false}, + }; + + for (const auto& test : cases) { + auto a = CSPSource(test.a); + auto b = CSPSource(test.b); + + EXPECT_EQ(CSPSourceSubsumes(a, b), test.expected_a_subsumes_b) + << test.a << " subsumes " << test.b << " should return " + << test.expected_a_subsumes_b; + EXPECT_EQ(CSPSourceSubsumes(b, a), test.expected_b_subsumes_a) + << test.b << " subsumes " << test.a << " should return " + << test.expected_b_subsumes_a; + + a->is_host_wildcard = true; + EXPECT_FALSE(CSPSourceSubsumes(b, a)) + << test.b << " should not subsume " << ToString(a); + + // If also |b| has a wildcard host, then the result should be the expected + // one. + b->is_host_wildcard = true; + EXPECT_EQ(CSPSourceSubsumes(b, a), test.expected_b_subsumes_a) + << ToString(b) << " subsumes " << ToString(a) << " should return " + << test.expected_b_subsumes_a; + } +} + +TEST(CSPSourceTest, HostWildcardSubsumes) { + const char* a = "http://*.example.org"; + const char* b = "http://www.example.org"; + const char* c = "http://example.org"; + const char* d = "https://*.example.org"; + + auto source_a = CSPSource(a); + auto source_b = CSPSource(b); + auto source_c = CSPSource(c); + auto source_d = CSPSource(d); + + // *.example.com subsumes www.example.com. + EXPECT_TRUE(CSPSourceSubsumes(source_a, source_b)) + << a << " should subsume " << b; + EXPECT_FALSE(CSPSourceSubsumes(source_b, source_a)) + << b << " should not subsume " << a; + + // *.example.com and example.com have no relations. + EXPECT_FALSE(CSPSourceSubsumes(source_a, source_c)) + << a << " should not subsume " << c; + EXPECT_FALSE(CSPSourceSubsumes(source_c, source_a)) + << c << " should not subsume " << a; + + // https://*.example.com and http://www.example.com have no relations. + EXPECT_FALSE(CSPSourceSubsumes(source_d, source_b)) + << d << " should not subsume " << b; + EXPECT_FALSE(CSPSourceSubsumes(source_b, source_d)) + << b << " should not subsume " << d; +} + +TEST(CSPSourceTest, PortWildcardSubsumes) { + const char* a = "http://example.org:*"; + const char* b = "http://example.org"; + const char* c = "https://example.org:*"; + + auto source_a = CSPSource(a); + auto source_b = CSPSource(b); + auto source_c = CSPSource(c); + + EXPECT_TRUE(CSPSourceSubsumes(source_a, source_b)) + << a << " should subsume " << b; + EXPECT_FALSE(CSPSourceSubsumes(source_b, source_a)) + << b << " should not subsume " << a; + + // https://example.com:* and http://example.com have no relations. + EXPECT_FALSE(CSPSourceSubsumes(source_b, source_c)) + << b << " should not subsume " << c; + EXPECT_FALSE(CSPSourceSubsumes(source_c, source_b)) + << c << " should not subsume " << b; +} + +TEST(CSPSourceTest, SchemesOnlySubsumes) { + struct TestCase { + const char* a; + const char* b; + bool expected; + } cases[] = { + // HTTP. + {"http:", "http:", true}, + {"http:", "https:", true}, + {"https:", "http:", false}, + {"https:", "https:", true}, + // WSS. + {"ws:", "ws:", true}, + {"ws:", "wss:", true}, + {"wss:", "ws:", false}, + {"wss:", "wss:", true}, + // Unequal. + {"ws:", "http:", false}, + {"http:", "ws:", false}, + {"http:", "about:", false}, + {"wss:", "https:", false}, + {"https:", "wss:", false}, + }; + + for (const auto& test : cases) { + auto source_a = CSPSource(test.a); + auto source_b = CSPSource(test.b); + EXPECT_EQ(CSPSourceSubsumes(source_a, source_b), test.expected) + << test.a << " subsumes " << test.b << " should return " + << test.expected; + } +} + TEST(CSPSourceTest, ToString) { { auto source = network::mojom::CSPSource::New( diff --git a/chromium/services/network/public/cpp/cookie_manager_mojom_traits.cc b/chromium/services/network/public/cpp/cookie_manager_mojom_traits.cc index d4897d169e5..56e5c6f6089 100644 --- a/chromium/services/network/public/cpp/cookie_manager_mojom_traits.cc +++ b/chromium/services/network/public/cpp/cookie_manager_mojom_traits.cc @@ -93,6 +93,8 @@ network::mojom::CookieEffectiveSameSite EnumTraits< return network::mojom::CookieEffectiveSameSite::kStrictMode; case net::CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE: return network::mojom::CookieEffectiveSameSite::kLaxModeAllowUnsafe; + case net::CookieEffectiveSameSite::UNDEFINED: + return network::mojom::CookieEffectiveSameSite::kUndefined; default: break; } @@ -117,6 +119,9 @@ bool EnumTraits<network::mojom::CookieEffectiveSameSite, case network::mojom::CookieEffectiveSameSite::kLaxModeAllowUnsafe: *output = net::CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE; return true; + case network::mojom::CookieEffectiveSameSite::kUndefined: + *output = net::CookieEffectiveSameSite::UNDEFINED; + return true; default: break; } @@ -403,37 +408,21 @@ bool StructTraits<network::mojom::CookieInclusionStatusDataView, return out->IsValid(); } -bool StructTraits< - network::mojom::CookieWithStatusDataView, - net::CookieWithStatus>::Read(network::mojom::CookieWithStatusDataView c, - net::CookieWithStatus* out) { - net::CanonicalCookie cookie; - net::CookieInclusionStatus status; - if (!c.ReadCookie(&cookie)) - return false; - if (!c.ReadStatus(&status)) - return false; - - *out = {cookie, status}; - - return true; -} - -bool StructTraits<network::mojom::CookieAndLineWithStatusDataView, - net::CookieAndLineWithStatus>:: - Read(network::mojom::CookieAndLineWithStatusDataView c, - net::CookieAndLineWithStatus* out) { +bool StructTraits<network::mojom::CookieAndLineWithAccessResultDataView, + net::CookieAndLineWithAccessResult>:: + Read(network::mojom::CookieAndLineWithAccessResultDataView c, + net::CookieAndLineWithAccessResult* out) { base::Optional<net::CanonicalCookie> cookie; std::string cookie_string; - net::CookieInclusionStatus status; + net::CookieAccessResult access_result; if (!c.ReadCookie(&cookie)) return false; if (!c.ReadCookieString(&cookie_string)) return false; - if (!c.ReadStatus(&status)) + if (!c.ReadAccessResult(&access_result)) return false; - *out = {cookie, cookie_string, status}; + *out = {cookie, cookie_string, access_result}; return true; } @@ -444,13 +433,16 @@ bool StructTraits< net::CookieAccessResult* out) { net::CookieEffectiveSameSite effective_same_site; net::CookieInclusionStatus status; + net::CookieAccessSemantics access_semantics; if (!c.ReadEffectiveSameSite(&effective_same_site)) return false; if (!c.ReadStatus(&status)) return false; + if (!c.ReadAccessSemantics(&access_semantics)) + return false; - *out = {effective_same_site, status}; + *out = {effective_same_site, status, access_semantics}; return true; } @@ -476,17 +468,16 @@ bool StructTraits< net::CookieChangeInfo>::Read(network::mojom::CookieChangeInfoDataView info, net::CookieChangeInfo* out) { net::CanonicalCookie cookie; - net::CookieAccessSemantics access_semantics = - net::CookieAccessSemantics::UNKNOWN; + net::CookieAccessResult access_result; net::CookieChangeCause cause = net::CookieChangeCause::EXPLICIT; if (!info.ReadCookie(&cookie)) return false; - if (!info.ReadAccessSemantics(&access_semantics)) + if (!info.ReadAccessResult(&access_result)) return false; if (!info.ReadCause(&cause)) return false; - *out = net::CookieChangeInfo(cookie, access_semantics, cause); + *out = net::CookieChangeInfo(cookie, access_result, cause); return true; } diff --git a/chromium/services/network/public/cpp/cookie_manager_mojom_traits.h b/chromium/services/network/public/cpp/cookie_manager_mojom_traits.h index 8b5243cafcf..9baa054951b 100644 --- a/chromium/services/network/public/cpp/cookie_manager_mojom_traits.h +++ b/chromium/services/network/public/cpp/cookie_manager_mojom_traits.h @@ -168,36 +168,22 @@ struct StructTraits<network::mojom::CookieInclusionStatusDataView, }; template <> -struct StructTraits<network::mojom::CookieWithStatusDataView, - net::CookieWithStatus> { - static const net::CanonicalCookie& cookie(const net::CookieWithStatus& c) { - return c.cookie; - } - static const net::CookieInclusionStatus& status( - const net::CookieWithStatus& c) { - return c.status; - } - static bool Read(network::mojom::CookieWithStatusDataView cookie, - net::CookieWithStatus* out); -}; - -template <> -struct StructTraits<network::mojom::CookieAndLineWithStatusDataView, - net::CookieAndLineWithStatus> { +struct StructTraits<network::mojom::CookieAndLineWithAccessResultDataView, + net::CookieAndLineWithAccessResult> { static const base::Optional<net::CanonicalCookie>& cookie( - const net::CookieAndLineWithStatus& c) { + const net::CookieAndLineWithAccessResult& c) { return c.cookie; } static const std::string& cookie_string( - const net::CookieAndLineWithStatus& c) { + const net::CookieAndLineWithAccessResult& c) { return c.cookie_string; } - static const net::CookieInclusionStatus& status( - const net::CookieAndLineWithStatus& c) { - return c.status; + static const net::CookieAccessResult& access_result( + const net::CookieAndLineWithAccessResult& c) { + return c.access_result; } - static bool Read(network::mojom::CookieAndLineWithStatusDataView cookie, - net::CookieAndLineWithStatus* out); + static bool Read(network::mojom::CookieAndLineWithAccessResultDataView cookie, + net::CookieAndLineWithAccessResult* out); }; template <> @@ -211,6 +197,10 @@ struct StructTraits<network::mojom::CookieAccessResultDataView, const net::CookieAccessResult& c) { return c.status; } + static const net::CookieAccessSemantics& access_semantics( + const net::CookieAccessResult& c) { + return c.access_semantics; + } static bool Read(network::mojom::CookieAccessResultDataView access_result, net::CookieAccessResult* out); }; @@ -236,9 +226,9 @@ struct StructTraits<network::mojom::CookieChangeInfoDataView, static const net::CanonicalCookie& cookie(const net::CookieChangeInfo& c) { return c.cookie; } - static net::CookieAccessSemantics access_semantics( + static const net::CookieAccessResult& access_result( const net::CookieChangeInfo& c) { - return c.access_semantics; + return c.access_result; } static net::CookieChangeCause cause(const net::CookieChangeInfo& c) { return c.cause; diff --git a/chromium/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc b/chromium/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc index d3be1aee9de..94a7557da24 100644 --- a/chromium/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc +++ b/chromium/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc @@ -92,19 +92,42 @@ TEST(CookieManagerTraitsTest, Roundtrips_CookieInclusionStatus) { &invalid, &copied)); } -TEST(CookieManagerTraitsTest, Roundtrips_CookieWithStatus) { +TEST(CookieManagerTraitsTest, Rountrips_CookieAccessResult) { + net::CookieAccessResult original = net::CookieAccessResult( + net::CookieEffectiveSameSite::LAX_MODE, + net::CookieInclusionStatus( + net::CookieInclusionStatus:: + EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX, + net::CookieInclusionStatus:: + WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT), + net::CookieAccessSemantics::LEGACY); + net::CookieAccessResult copied; + + EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieAccessResult>( + &original, &copied)); + + EXPECT_EQ(original.effective_same_site, copied.effective_same_site); + EXPECT_TRUE(copied.status.HasExactlyExclusionReasonsForTesting( + {net::CookieInclusionStatus:: + EXCLUDE_SAMESITE_UNSPECIFIED_TREATED_AS_LAX})); + EXPECT_TRUE(copied.status.HasExactlyWarningReasonsForTesting( + {net::CookieInclusionStatus:: + WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT})); +} + +TEST(CookieManagerTraitsTest, Rountrips_CookieWithAccessResult) { net::CanonicalCookie original_cookie( "A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), /* secure = */ true, /* http_only = */ false, net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_LOW); - net::CookieWithStatus original = {original_cookie, - net::CookieInclusionStatus()}; + net::CookieWithAccessResult original = {original_cookie, + net::CookieAccessResult()}; + net::CookieWithAccessResult copied; - net::CookieWithStatus copied; - - EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::CookieWithStatus>( - &original, &copied)); + EXPECT_TRUE( + mojo::test::SerializeAndDeserialize<mojom::CookieWithAccessResult>( + &original, &copied)); EXPECT_EQ(original.cookie.Name(), copied.cookie.Name()); EXPECT_EQ(original.cookie.Value(), copied.cookie.Value()); @@ -117,7 +140,39 @@ TEST(CookieManagerTraitsTest, Roundtrips_CookieWithStatus) { EXPECT_EQ(original.cookie.IsHttpOnly(), copied.cookie.IsHttpOnly()); EXPECT_EQ(original.cookie.SameSite(), copied.cookie.SameSite()); EXPECT_EQ(original.cookie.Priority(), copied.cookie.Priority()); - EXPECT_EQ(original.status, copied.status); + EXPECT_EQ(original.access_result.effective_same_site, + copied.access_result.effective_same_site); + EXPECT_EQ(original.access_result.status, copied.access_result.status); +} + +TEST(CookieManagerTraitsTest, Rountrips_CookieAndLineWithAccessResult) { + net::CanonicalCookie original_cookie( + "A", "B", "x.y", "/path", base::Time(), base::Time(), base::Time(), + /* secure = */ true, /* http_only = */ false, + net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_LOW); + + net::CookieAndLineWithAccessResult original(original_cookie, "cookie-string", + net::CookieAccessResult()); + net::CookieAndLineWithAccessResult copied; + + EXPECT_TRUE( + mojo::test::SerializeAndDeserialize<mojom::CookieAndLineWithAccessResult>( + &original, &copied)); + + EXPECT_EQ(original.cookie->Name(), copied.cookie->Name()); + EXPECT_EQ(original.cookie->Value(), copied.cookie->Value()); + EXPECT_EQ(original.cookie->Domain(), copied.cookie->Domain()); + EXPECT_EQ(original.cookie->Path(), copied.cookie->Path()); + EXPECT_EQ(original.cookie->CreationDate(), copied.cookie->CreationDate()); + EXPECT_EQ(original.cookie->LastAccessDate(), copied.cookie->LastAccessDate()); + EXPECT_EQ(original.cookie->ExpiryDate(), copied.cookie->ExpiryDate()); + EXPECT_EQ(original.cookie->IsSecure(), copied.cookie->IsSecure()); + EXPECT_EQ(original.cookie->IsHttpOnly(), copied.cookie->IsHttpOnly()); + EXPECT_EQ(original.cookie->SameSite(), copied.cookie->SameSite()); + EXPECT_EQ(original.cookie->Priority(), copied.cookie->Priority()); + EXPECT_EQ(original.access_result.effective_same_site, + copied.access_result.effective_same_site); + EXPECT_EQ(original.cookie_string, copied.cookie_string); } TEST(CookieManagerTraitsTest, Roundtrips_CookieSameSite) { @@ -131,6 +186,20 @@ TEST(CookieManagerTraitsTest, Roundtrips_CookieSameSite) { } } +TEST(CookieManagerTraitsTest, Roundtrips_CookieEffectiveSameSite) { + for (net::CookieEffectiveSameSite cookie_state : + {net::CookieEffectiveSameSite::NO_RESTRICTION, + net::CookieEffectiveSameSite::LAX_MODE, + net::CookieEffectiveSameSite::STRICT_MODE, + net::CookieEffectiveSameSite::LAX_MODE_ALLOW_UNSAFE, + net::CookieEffectiveSameSite::UNDEFINED}) { + net::CookieEffectiveSameSite roundtrip; + ASSERT_TRUE(SerializeAndDeserializeEnum<mojom::CookieEffectiveSameSite>( + cookie_state, &roundtrip)); + EXPECT_EQ(cookie_state, roundtrip); + } +} + TEST(CookieManagerTraitsTest, Roundtrips_ContextType) { using ContextType = net::CookieOptions::SameSiteCookieContext::ContextType; for (ContextType context_type : @@ -232,9 +301,12 @@ TEST(CookieManagerTraitsTest, Roundtrips_CookieChangeInfo) { /* secure = */ false, /* http_only = */ false, net::CookieSameSite::UNSPECIFIED, net::COOKIE_PRIORITY_LOW); - net::CookieChangeInfo original(original_cookie, - net::CookieAccessSemantics::LEGACY, - net::CookieChangeCause::EXPLICIT); + net::CookieChangeInfo original( + original_cookie, + net::CookieAccessResult(net::CookieEffectiveSameSite::UNDEFINED, + net::CookieInclusionStatus(), + net::CookieAccessSemantics::LEGACY), + net::CookieChangeCause::EXPLICIT); net::CookieChangeInfo copied; @@ -252,7 +324,8 @@ TEST(CookieManagerTraitsTest, Roundtrips_CookieChangeInfo) { EXPECT_EQ(original.cookie.IsHttpOnly(), copied.cookie.IsHttpOnly()); EXPECT_EQ(original.cookie.SameSite(), copied.cookie.SameSite()); EXPECT_EQ(original.cookie.Priority(), copied.cookie.Priority()); - EXPECT_EQ(original.access_semantics, copied.access_semantics); + EXPECT_EQ(original.access_result.access_semantics, + copied.access_result.access_semantics); EXPECT_EQ(original.cause, copied.cause); } diff --git a/chromium/services/network/public/cpp/cors/cors.cc b/chromium/services/network/public/cpp/cors/cors.cc index 6e12d8f2970..92a86e4e6d5 100644 --- a/chromium/services/network/public/cpp/cors/cors.cc +++ b/chromium/services/network/public/cpp/cors/cors.cc @@ -392,17 +392,9 @@ bool IsCorsSafelistedContentType(const std::string& media_type) { return IsCorsSafelistedLowerCaseContentType(base::ToLowerASCII(media_type)); } -bool IsCorsSafelistedHeader( - const std::string& name, - const std::string& value, - const base::flat_set<std::string>& extra_safelisted_header_names) { +bool IsCorsSafelistedHeader(const std::string& name, const std::string& value) { const std::string lower_name = base::ToLowerASCII(name); - if (extra_safelisted_header_names.find(lower_name) != - extra_safelisted_header_names.end()) { - return true; - } - // If |value|’s length is greater than 128, then return false. if (value.size() > 128) return false; @@ -535,8 +527,7 @@ std::vector<std::string> CorsUnsafeRequestHeaderNames( std::vector<std::string> CorsUnsafeNotForbiddenRequestHeaderNames( const net::HttpRequestHeaders::HeaderVector& headers, - bool is_revalidating, - const base::flat_set<std::string>& extra_safelisted_header_names) { + bool is_revalidating) { std::vector<std::string> header_names; std::vector<std::string> potentially_unsafe_names; @@ -555,8 +546,7 @@ std::vector<std::string> CorsUnsafeNotForbiddenRequestHeaderNames( continue; } } - if (!IsCorsSafelistedHeader(name, header.value, - extra_safelisted_header_names)) { + if (!IsCorsSafelistedHeader(name, header.value)) { header_names.push_back(name); } else { potentially_unsafe_names.push_back(name); diff --git a/chromium/services/network/public/cpp/cors/cors.h b/chromium/services/network/public/cpp/cors/cors.h index bd3e9aee232..813abc9022b 100644 --- a/chromium/services/network/public/cpp/cors/cors.h +++ b/chromium/services/network/public/cpp/cors/cors.h @@ -108,10 +108,7 @@ bool IsCorsSafelistedMethod(const std::string& method); COMPONENT_EXPORT(NETWORK_CPP) bool IsCorsSafelistedContentType(const std::string& name); COMPONENT_EXPORT(NETWORK_CPP) -bool IsCorsSafelistedHeader( - const std::string& name, - const std::string& value, - const base::flat_set<std::string>& extra_safelisted_header_names = {}); +bool IsCorsSafelistedHeader(const std::string& name, const std::string& value); COMPONENT_EXPORT(NETWORK_CPP) bool IsNoCorsSafelistedHeaderName(const std::string& name); COMPONENT_EXPORT(NETWORK_CPP) @@ -138,8 +135,7 @@ std::vector<std::string> CorsUnsafeRequestHeaderNames( COMPONENT_EXPORT(NETWORK_CPP) std::vector<std::string> CorsUnsafeNotForbiddenRequestHeaderNames( const net::HttpRequestHeaders::HeaderVector& headers, - bool is_revalidating, - const base::flat_set<std::string>& extra_safelisted_header_names = {}); + bool is_revalidating); // Checks forbidden method in the fetch spec. // See https://fetch.spec.whatwg.org/#forbidden-method. diff --git a/chromium/services/network/public/cpp/cors/cors_legacy.h b/chromium/services/network/public/cpp/cors/cors_legacy.h index af6c3bbe553..6da3b705e1b 100644 --- a/chromium/services/network/public/cpp/cors/cors_legacy.h +++ b/chromium/services/network/public/cpp/cors/cors_legacy.h @@ -22,12 +22,12 @@ namespace cors { // TODO(toyoshim): Remove all functions after Network Service is enabled. namespace legacy { -// Registers whitelisted secure origins and hostname patterns for CORS checks in +// Registers allowlisted secure origins and hostname patterns for CORS checks in // CorsURLLoader. COMPONENT_EXPORT(NETWORK_CPP) void RegisterSecureOrigins(const std::vector<std::string>& secure_origins); -// Refers the registered whitelisted secure origins and hostname patterns. +// Refers the registered allowlisted secure origins and hostname patterns. COMPONENT_EXPORT(NETWORK_CPP) const std::vector<std::string>& GetSecureOrigins(); diff --git a/chromium/services/network/public/cpp/cors/origin_access_list.h b/chromium/services/network/public/cpp/cors/origin_access_list.h index 4b6b8826fe3..e867106dc65 100644 --- a/chromium/services/network/public/cpp/cors/origin_access_list.h +++ b/chromium/services/network/public/cpp/cors/origin_access_list.h @@ -29,7 +29,7 @@ class CorsOriginAccessPatterns; namespace cors { // A class to manage origin access allow / block lists. If these lists conflict, -// blacklisting is respected. These lists are managed per source-origin basis. +// blocklisting is respected. These lists are managed per source-origin basis. class COMPONENT_EXPORT(NETWORK_CPP) OriginAccessList { public: using CorsOriginPatternPtr = mojo::StructPtr<mojom::CorsOriginPattern>; diff --git a/chromium/services/network/public/cpp/cors/preflight_result.cc b/chromium/services/network/public/cpp/cors/preflight_result.cc index fe8e60f917b..022ff804e41 100644 --- a/chromium/services/network/public/cpp/cors/preflight_result.cc +++ b/chromium/services/network/public/cpp/cors/preflight_result.cc @@ -144,8 +144,7 @@ base::Optional<CorsErrorStatus> PreflightResult::EnsureAllowedCrossOriginMethod( base::Optional<CorsErrorStatus> PreflightResult::EnsureAllowedCrossOriginHeaders( const net::HttpRequestHeaders& headers, - bool is_revalidating, - const base::flat_set<std::string>& extra_safelisted_header_names) const { + bool is_revalidating) const { if (!credentials_ && headers_.find("*") != headers_.end()) return base::nullopt; @@ -153,8 +152,7 @@ PreflightResult::EnsureAllowedCrossOriginHeaders( // beforehand. But user-agents may add these headers internally, and it's // fine. for (const auto& name : CorsUnsafeNotForbiddenRequestHeaderNames( - headers.GetHeaderVector(), is_revalidating, - extra_safelisted_header_names)) { + headers.GetHeaderVector(), is_revalidating)) { // Header list check is performed in case-insensitive way. Here, we have a // parsed header list set in lower case, and search each header in lower // case. diff --git a/chromium/services/network/public/cpp/cors/preflight_result.h b/chromium/services/network/public/cpp/cors/preflight_result.h index c6ed14f3306..49ef1997066 100644 --- a/chromium/services/network/public/cpp/cors/preflight_result.h +++ b/chromium/services/network/public/cpp/cors/preflight_result.h @@ -57,9 +57,7 @@ class COMPONENT_EXPORT(NETWORK_CPP) PreflightResult final { // JavaScript-initiated requests. base::Optional<CorsErrorStatus> EnsureAllowedCrossOriginHeaders( const net::HttpRequestHeaders& headers, - bool is_revalidating, - const base::flat_set<std::string>& extra_safelisted_header_names = {}) - const; + bool is_revalidating) const; // Checks if this entry is expired. bool IsExpired() const; diff --git a/chromium/services/network/public/cpp/cross_origin_opener_policy.cc b/chromium/services/network/public/cpp/cross_origin_opener_policy.cc index c00cacec0b4..880dd98c33d 100644 --- a/chromium/services/network/public/cpp/cross_origin_opener_policy.cc +++ b/chromium/services/network/public/cpp/cross_origin_opener_policy.cc @@ -25,4 +25,17 @@ bool CrossOriginOpenerPolicy::operator==( report_only_reporting_endpoint == other.report_only_reporting_endpoint; } +bool IsAccessFromCoopPage(mojom::CoopAccessReportType type) { + switch (type) { + case mojom::CoopAccessReportType::kAccessFromCoopPageToOpener: + case mojom::CoopAccessReportType::kAccessFromCoopPageToOpenee: + case mojom::CoopAccessReportType::kAccessFromCoopPageToOther: + return true; + case mojom::CoopAccessReportType::kAccessToCoopPageFromOpener: + case mojom::CoopAccessReportType::kAccessToCoopPageFromOpenee: + case mojom::CoopAccessReportType::kAccessToCoopPageFromOther: + return false; + } +} + } // namespace network diff --git a/chromium/services/network/public/cpp/cross_origin_opener_policy.h b/chromium/services/network/public/cpp/cross_origin_opener_policy.h index b41f580e53f..5e4e16a8d22 100644 --- a/chromium/services/network/public/cpp/cross_origin_opener_policy.h +++ b/chromium/services/network/public/cpp/cross_origin_opener_policy.h @@ -31,6 +31,9 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) CrossOriginOpenerPolicy final { base::Optional<std::string> report_only_reporting_endpoint; }; +COMPONENT_EXPORT(NETWORK_CPP_BASE) +bool IsAccessFromCoopPage(mojom::CoopAccessReportType); + } // namespace network #endif // SERVICES_NETWORK_PUBLIC_CPP_CROSS_ORIGIN_OPENER_POLICY_H_ diff --git a/chromium/services/network/public/cpp/cross_origin_opener_policy_parser.cc b/chromium/services/network/public/cpp/cross_origin_opener_policy_parser.cc index 599a0284e20..3d8c8b93b63 100644 --- a/chromium/services/network/public/cpp/cross_origin_opener_policy_parser.cc +++ b/chromium/services/network/public/cpp/cross_origin_opener_policy_parser.cc @@ -9,7 +9,6 @@ #include "net/http/http_response_headers.h" #include "net/http/structured_headers.h" #include "services/network/public/cpp/features.h" - namespace network { namespace { @@ -50,20 +49,34 @@ ParseHeader(base::StringPiece header_value) { } // namespace CrossOriginOpenerPolicy ParseCrossOriginOpenerPolicy( - const net::HttpResponseHeaders& headers) { + const net::HttpResponseHeaders& headers, + const CrossOriginEmbedderPolicy& coep) { CrossOriginOpenerPolicy coop; + + // This is the single line of code disabling COOP globally. if (!base::FeatureList::IsEnabled(features::kCrossOriginOpenerPolicy)) return coop; - + std::string header_value; if (headers.GetNormalizedHeader(kCrossOriginOpenerPolicyHeader, &header_value)) { std::tie(coop.value, coop.reporting_endpoint) = ParseHeader(header_value); + if (coop.value == mojom::CrossOriginOpenerPolicyValue::kSameOrigin && + coep.value == mojom::CrossOriginEmbedderPolicyValue::kRequireCorp) + coop.value = mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep; } if (headers.GetNormalizedHeader(kCrossOriginOpenerPolicyHeaderReportOnly, &header_value)) { std::tie(coop.report_only_value, coop.report_only_reporting_endpoint) = ParseHeader(header_value); + if (coop.report_only_value == + mojom::CrossOriginOpenerPolicyValue::kSameOrigin && + (coep.value == mojom::CrossOriginEmbedderPolicyValue::kRequireCorp || + coep.report_only_value == + mojom::CrossOriginEmbedderPolicyValue::kRequireCorp)) { + coop.report_only_value = + mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep; + } } return coop; } diff --git a/chromium/services/network/public/cpp/cross_origin_opener_policy_parser.h b/chromium/services/network/public/cpp/cross_origin_opener_policy_parser.h index b7b4e1ff0f5..caa0c435961 100644 --- a/chromium/services/network/public/cpp/cross_origin_opener_policy_parser.h +++ b/chromium/services/network/public/cpp/cross_origin_opener_policy_parser.h @@ -5,6 +5,7 @@ #ifndef SERVICES_NETWORK_PUBLIC_CPP_CROSS_ORIGIN_OPENER_POLICY_PARSER_H_ #define SERVICES_NETWORK_PUBLIC_CPP_CROSS_ORIGIN_OPENER_POLICY_PARSER_H_ +#include "services/network/public/mojom/cross_origin_embedder_policy.mojom.h" #include "services/network/public/mojom/cross_origin_opener_policy.mojom.h" namespace net { @@ -19,7 +20,8 @@ namespace network { // services/network/content_security_policy_fuzzer.cc for an example. COMPONENT_EXPORT(NETWORK_CPP) CrossOriginOpenerPolicy ParseCrossOriginOpenerPolicy( - const net::HttpResponseHeaders& headers); + const net::HttpResponseHeaders& headers, + const CrossOriginEmbedderPolicy& coep); } // namespace network diff --git a/chromium/services/network/public/cpp/cross_origin_opener_policy_parser_unittest.cc b/chromium/services/network/public/cpp/cross_origin_opener_policy_parser_unittest.cc index 6f4ddcbb701..b84ebbe23e7 100644 --- a/chromium/services/network/public/cpp/cross_origin_opener_policy_parser_unittest.cc +++ b/chromium/services/network/public/cpp/cross_origin_opener_policy_parser_unittest.cc @@ -10,6 +10,7 @@ #include "base/test/scoped_feature_list.h" #include "net/http/http_response_headers.h" #include "services/network/public/cpp/features.h" +#include "services/network/public/mojom/cross_origin_embedder_policy.mojom.h" #include "testing/gtest/include/gtest/gtest.h" namespace network { @@ -23,123 +24,150 @@ TEST(CrossOriginOpenerPolicyTest, Parse) { constexpr auto kSameOriginAllowPopups = CrossOriginOpenerPolicyValue::kSameOriginAllowPopups; constexpr auto kUnsafeNone = CrossOriginOpenerPolicyValue::kUnsafeNone; + constexpr auto kSameOriginPlusCoep = + CrossOriginOpenerPolicyValue::kSameOriginPlusCoep; + constexpr auto kCoepNone = mojom::CrossOriginEmbedderPolicyValue::kNone; + constexpr auto kCoepCorp = + mojom::CrossOriginEmbedderPolicyValue::kRequireCorp; const auto kNoHeader = base::Optional<std::string>(); const auto kNoEndpoint = kNoHeader; const struct { base::Optional<std::string> raw_coop_string; + mojom::CrossOriginEmbedderPolicyValue coep_value; base::Optional<std::string> raw_coop_report_only_string; + mojom::CrossOriginEmbedderPolicyValue coep_report_only_value; base::Optional<std::string> expected_endpoint; CrossOriginOpenerPolicyValue expected_policy; base::Optional<std::string> expected_endpoit_report_only; CrossOriginOpenerPolicyValue expected_policy_report_only; } kTestCases[] = { - {"same-origin", kNoHeader, kNoEndpoint, kSameOrigin, kNoEndpoint, - kUnsafeNone}, - {"same-origin-allow-popups", kNoHeader, kNoEndpoint, + {"same-origin", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, kSameOrigin, + kNoEndpoint, kUnsafeNone}, + {"same-origin-allow-popups", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, kSameOriginAllowPopups, kNoEndpoint, kUnsafeNone}, - {"unsafe-none", kNoHeader, kNoEndpoint, kUnsafeNone, kNoEndpoint, - kUnsafeNone}, + {"unsafe-none", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, kUnsafeNone, + kNoEndpoint, kUnsafeNone}, // Leading whitespaces. - {" same-origin", kNoHeader, kNoEndpoint, kSameOrigin, kNoEndpoint, - kUnsafeNone}, + {" same-origin", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, + kSameOrigin, kNoEndpoint, kUnsafeNone}, // Leading character tabulation. - {"\tsame-origin", kNoHeader, kNoEndpoint, kSameOrigin, kNoEndpoint, - kUnsafeNone}, + {"\tsame-origin", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, + kSameOrigin, kNoEndpoint, kUnsafeNone}, // Trailing whitespaces. - {"same-origin-allow-popups ", kNoHeader, kNoEndpoint, - kSameOriginAllowPopups, kNoEndpoint, kUnsafeNone}, + {"same-origin-allow-popups ", kCoepNone, kNoHeader, kCoepNone, + kNoEndpoint, kSameOriginAllowPopups, kNoEndpoint, kUnsafeNone}, // Empty string. - {"", kNoHeader, kNoEndpoint, kUnsafeNone, kNoEndpoint, kUnsafeNone}, + {"", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, kUnsafeNone, + kNoEndpoint, kUnsafeNone}, // Only whitespaces. - {" ", kNoHeader, kNoEndpoint, kUnsafeNone, kNoEndpoint, kUnsafeNone}, + {" ", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, kUnsafeNone, + kNoEndpoint, kUnsafeNone}, // Invalid same-site value - {"same-site", kNoHeader, kNoEndpoint, kUnsafeNone, kNoEndpoint, - kUnsafeNone}, + {"same-site", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, kUnsafeNone, + kNoEndpoint, kUnsafeNone}, // Misspelling. - {"some-origin", kNoHeader, kNoEndpoint, kUnsafeNone, kNoEndpoint, - kUnsafeNone}, + {"some-origin", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, kUnsafeNone, + kNoEndpoint, kUnsafeNone}, // Trailing line-tab. - {"same-origin\x0B", kNoHeader, kNoEndpoint, kUnsafeNone, kNoEndpoint, - kUnsafeNone}, + {"same-origin\x0B", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, + kUnsafeNone, kNoEndpoint, kUnsafeNone}, // Adding report endpoint. - {"same-origin; report-to=\"endpoint\"", kNoHeader, "endpoint", - kSameOrigin, kNoEndpoint, kUnsafeNone}, + {"same-origin; report-to=\"endpoint\"", kCoepNone, kNoHeader, kCoepNone, + "endpoint", kSameOrigin, kNoEndpoint, kUnsafeNone}, // Extraneous parameter, ignored. - {"same-origin; report-to=\"endpoint\"; foo=bar", kNoHeader, "endpoint", - kSameOrigin, kNoEndpoint, kUnsafeNone}, + {"same-origin; report-to=\"endpoint\"; foo=bar", kCoepNone, kNoHeader, + kCoepNone, "endpoint", kSameOrigin, kNoEndpoint, kUnsafeNone}, // Multiple endpoints - {"same-origin; report-to=\"endpoint\"; report-to=\"foo\"", kNoHeader, - "foo", kSameOrigin, kNoEndpoint, kUnsafeNone}, + {"same-origin; report-to=\"endpoint\"; report-to=\"foo\"", kCoepNone, + kNoHeader, kCoepNone, "foo", kSameOrigin, kNoEndpoint, kUnsafeNone}, // Leading spaces in the reporting endpoint. - {"same-origin-allow-popups; report-to=\"endpoint\"", kNoHeader, - "endpoint", kSameOriginAllowPopups, kNoEndpoint, kUnsafeNone}, + {"same-origin-allow-popups; report-to=\"endpoint\"", kCoepNone, + kNoHeader, kCoepNone, "endpoint", kSameOriginAllowPopups, kNoEndpoint, + kUnsafeNone}, // Unsafe-none with endpoint. - {"unsafe-none; report-to=\"endpoint\"", kNoHeader, "endpoint", - kUnsafeNone, kNoEndpoint, kUnsafeNone}, + {"unsafe-none; report-to=\"endpoint\"", kCoepNone, kNoHeader, kCoepNone, + "endpoint", kUnsafeNone, kNoEndpoint, kUnsafeNone}, // Unknown parameters should just be ignored. - {"same-origin; invalidparameter=\"unknown\"", kNoHeader, kNoEndpoint, - kSameOrigin, kNoEndpoint, kUnsafeNone}, + {"same-origin; invalidparameter=\"unknown\"", kCoepNone, kNoHeader, + kCoepNone, kNoEndpoint, kSameOrigin, kNoEndpoint, kUnsafeNone}, // Non-string report-to value. - {"same-origin; report-to=other-endpoint", kNoHeader, kNoEndpoint, - kSameOrigin, kNoEndpoint, kUnsafeNone}, + {"same-origin; report-to=other-endpoint", kCoepNone, kNoHeader, kCoepNone, + kNoEndpoint, kSameOrigin, kNoEndpoint, kUnsafeNone}, // Malformated parameter value. - {"same-origin-allow-popups; foo", kNoHeader, kNoEndpoint, - kSameOriginAllowPopups, kNoEndpoint, kUnsafeNone}, + {"same-origin-allow-popups; foo", kCoepNone, kNoHeader, kCoepNone, + kNoEndpoint, kSameOriginAllowPopups, kNoEndpoint, kUnsafeNone}, // Report to empty string endpoint. - {"same-origin; report-to=\"\"", kNoHeader, "", kSameOrigin, kNoEndpoint, - kUnsafeNone}, + {"same-origin; report-to=\"\"", kCoepNone, kNoHeader, kCoepNone, "", + kSameOrigin, kNoEndpoint, kUnsafeNone}, // Empty parameter value, parsing fails. - {"same-origin; report-to=", kNoHeader, kNoEndpoint, kUnsafeNone, - kNoEndpoint, kUnsafeNone}, + {"same-origin; report-to=", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, + kUnsafeNone, kNoEndpoint, kUnsafeNone}, // Empty parameter key, parsing fails. - {"same-origin; =\"\"", kNoHeader, kNoEndpoint, kUnsafeNone, kNoEndpoint, - kUnsafeNone}, + {"same-origin; =\"\"", kCoepNone, kNoHeader, kCoepNone, kNoEndpoint, + kUnsafeNone, kNoEndpoint, kUnsafeNone}, // Report only same origin header. - {kNoHeader, "same-origin", kNoEndpoint, kUnsafeNone, kNoEndpoint, - kSameOrigin}, + {kNoHeader, kCoepNone, "same-origin", kCoepNone, kNoEndpoint, kUnsafeNone, + kNoEndpoint, kSameOrigin}, // Report only header. - {kNoHeader, "same-origin-allow-popups", kNoEndpoint, kUnsafeNone, - kNoEndpoint, kSameOriginAllowPopups}, + {kNoHeader, kCoepNone, "same-origin-allow-popups", kCoepNone, kNoEndpoint, + kUnsafeNone, kNoEndpoint, kSameOriginAllowPopups}, // Report only header. - {kNoHeader, "unsafe-none", kNoEndpoint, kUnsafeNone, kNoEndpoint, - kUnsafeNone}, + {kNoHeader, kCoepNone, "unsafe-none", kCoepNone, kNoEndpoint, kUnsafeNone, + kNoEndpoint, kUnsafeNone}, // Report only same origin header with endpoint. - {kNoHeader, "same-origin; report-to=\"endpoint\"", kNoEndpoint, - kUnsafeNone, "endpoint", kSameOrigin}, + {kNoHeader, kCoepNone, "same-origin; report-to=\"endpoint\"", kCoepNone, + kNoEndpoint, kUnsafeNone, "endpoint", kSameOrigin}, // Report only allow popups with endpoint. - {kNoHeader, "same-origin-allow-popups; report-to=\"endpoint\"", - kNoEndpoint, kUnsafeNone, "endpoint", kSameOriginAllowPopups}, + {kNoHeader, kCoepNone, "same-origin-allow-popups; report-to=\"endpoint\"", + kCoepNone, kNoEndpoint, kUnsafeNone, "endpoint", kSameOriginAllowPopups}, // Report only unsafe none with endpoint. - {kNoHeader, "unsafe-none; report-to=\"endpoint\" ", kNoEndpoint, - kUnsafeNone, "endpoint", kUnsafeNone}, + {kNoHeader, kCoepNone, "unsafe-none; report-to=\"endpoint\" ", kCoepNone, + kNoEndpoint, kUnsafeNone, "endpoint", kUnsafeNone}, // Invalid COOP header with valid COOP report only. - {"INVALID HEADER", "same-origin; report-to=\"endpoint\"", kNoEndpoint, - kUnsafeNone, "endpoint", kSameOrigin}, + {"INVALID HEADER", kCoepNone, "same-origin; report-to=\"endpoint\"", + kCoepNone, kNoEndpoint, kUnsafeNone, "endpoint", kSameOrigin}, // Unsafe none COOP and allow popups COOP report only. - {"unsafe-none", "same-origin-allow-popups; report-to=\"endpoint\"", + {"unsafe-none", kCoepNone, + "same-origin-allow-popups; report-to=\"endpoint\"", kCoepNone, kNoEndpoint, kUnsafeNone, "endpoint", kSameOriginAllowPopups}, // Same-origin-allow-popups coop + same-origin report-only. - {"same-origin-allow-popups", "same-origin; report-to=\"endpoint\" ", - kNoEndpoint, kSameOriginAllowPopups, "endpoint", kSameOrigin}, + {"same-origin-allow-popups", kCoepNone, + "same-origin; report-to=\"endpoint\" ", kCoepNone, kNoEndpoint, + kSameOriginAllowPopups, "endpoint", kSameOrigin}, // Same-origin-allow-popups coop + same-origin report-only, with reporting // on both. - {"same-origin-allow-popups; report-to=\"endpointA\"", - "same-origin; report-to=\"endpointB\" ", "endpointA", + {"same-origin-allow-popups; report-to=\"endpointA\"", kCoepNone, + "same-origin; report-to=\"endpointB\" ", kCoepNone, "endpointA", kSameOriginAllowPopups, "endpointB", kSameOrigin}, // Same-origin-allow-popups coop + same-origin report-only, with reporting // on both, same endpoint. - {"same-origin-allow-popups; report-to=\"endpoint\"", - "same-origin; report-to=\"endpoint\" ", "endpoint", + {"same-origin-allow-popups; report-to=\"endpoint\"", kCoepNone, + "same-origin; report-to=\"endpoint\" ", kCoepNone, "endpoint", kSameOriginAllowPopups, "endpoint", kSameOrigin}, // Unsafe coop + same-origin report-only, with reporting on both. - {"unsafe-none; report-to=\"endpoint\"", - "same-origin; report-to=\"endpoint\" ", "endpoint", kUnsafeNone, - "endpoint", kSameOrigin}, + {"unsafe-none; report-to=\"endpoint\"", kCoepNone, + "same-origin; report-to=\"endpoint\" ", kCoepNone, "endpoint", + kUnsafeNone, "endpoint", kSameOrigin}, // Same-origin with reporting COOP, invalid COOP report-only. - {"same-origin; report-to=\"endpoint\"", "INVALID HEADER", "endpoint", - kSameOrigin, kNoEndpoint, kUnsafeNone}, + {"same-origin; report-to=\"endpoint\"", kCoepNone, "INVALID HEADER", + kCoepNone, "endpoint", kSameOrigin, kNoEndpoint, kUnsafeNone}, + // Same-origin with COEP + {"same-origin", kCoepCorp, kNoHeader, kCoepNone, kNoEndpoint, + kSameOriginPlusCoep, kNoEndpoint, kUnsafeNone}, + // Same-origin-allow-popups with COEP + {"same-origin-allow-popups", kCoepCorp, kNoHeader, kCoepNone, kNoEndpoint, + kSameOriginAllowPopups, kNoEndpoint, kUnsafeNone}, + // Same-origin with report only COEP + {"same-origin", kCoepNone, kNoHeader, kCoepCorp, kNoEndpoint, kSameOrigin, + kNoEndpoint, kUnsafeNone}, + // reporting Same-origin with COEP + {kNoHeader, kCoepCorp, "same-origin", kCoepNone, kNoEndpoint, kUnsafeNone, + kNoEndpoint, kSameOriginPlusCoep}, + // reporting Same-origin with reporting COEP + {kNoHeader, kCoepNone, "same-origin", kCoepCorp, kNoEndpoint, kUnsafeNone, + kNoEndpoint, kSameOriginPlusCoep}, }; for (const auto& test_case : kTestCases) { @@ -164,7 +192,11 @@ TEST(CrossOriginOpenerPolicyTest, Parse) { headers->AddHeader("cross-origin-opener-policy-report-only", *test_case.raw_coop_report_only_string); } - auto parsed_policy = ParseCrossOriginOpenerPolicy(*headers); + network::CrossOriginEmbedderPolicy coep; + coep.value = test_case.coep_value; + coep.report_only_value = test_case.coep_report_only_value; + + auto parsed_policy = ParseCrossOriginOpenerPolicy(*headers, coep); EXPECT_EQ(test_case.expected_endpoint, parsed_policy.reporting_endpoint); EXPECT_EQ(test_case.expected_policy, parsed_policy.value); EXPECT_EQ(test_case.expected_endpoit_report_only, diff --git a/chromium/services/network/cross_origin_read_blocking.cc b/chromium/services/network/public/cpp/cross_origin_read_blocking.cc index c0eb842c5bc..5892de9625e 100644 --- a/chromium/services/network/cross_origin_read_blocking.cc +++ b/chromium/services/network/public/cpp/cross_origin_read_blocking.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "services/network/cross_origin_read_blocking.h" +#include "services/network/public/cpp/cross_origin_read_blocking.h" #include <stddef.h> @@ -137,7 +137,8 @@ size_t FindFirstJavascriptLineTerminator(const base::StringPiece& hay, // TODO(lukasza): Prevent matching 3 bytes that span/straddle 2 UTF8 // characters. base::StringPiece substr = hay.substr(pos); - if (substr.starts_with("\u2028") || substr.starts_with("\u2029")) + if (base::StartsWith(substr, "\u2028") || + base::StartsWith(substr, "\u2029")) break; pos++; // Skip the \xe2 character. @@ -158,8 +159,8 @@ size_t FindFirstJavascriptLineTerminator(const base::StringPiece& hay, // terminating character. SniffingResult MaybeSkipHtmlComment(StringPiece* data) { constexpr StringPiece kStartString = "<!--"; - if (!data->starts_with(kStartString)) { - if (kStartString.starts_with(*data)) + if (!base::StartsWith(*data, kStartString)) { + if (base::StartsWith(kStartString, *data)) return CrossOriginReadBlocking::kMaybe; return CrossOriginReadBlocking::kNo; } @@ -220,11 +221,6 @@ void BlockResponseHeaders( headers->RemoveHeaders(names_of_headers_to_remove); } -std::set<int>& GetPluginProxyingProcesses() { - static base::NoDestructor<std::set<int>> set; - return *set; -} - // The function below returns a set of MIME types below may be blocked by CORB // without any confirmation sniffing (in contrast to HTML/JSON/XML which require // confirmation sniffing because images, scripts, etc. are frequently @@ -257,7 +253,7 @@ base::flat_set<std::string>& GetNeverSniffedMimeTypes() { // The types below (zip, protobuf, etc.) are based on most commonly used // content types according to HTTP Archive - see: // https://github.com/whatwg/fetch/issues/860#issuecomment-457330454 - "application/gzip", + "application/gzip", "application/x-gzip", "application/x-protobuf", "application/zip", @@ -325,6 +321,7 @@ base::flat_set<std::string>& GetNeverSniffedMimeTypes() { } // namespace +// static MimeType CrossOriginReadBlocking::GetCanonicalMimeType( base::StringPiece mime_type) { // Checking for image/svg+xml and application/dash+xml early ensures that they @@ -365,6 +362,7 @@ MimeType CrossOriginReadBlocking::GetCanonicalMimeType( return MimeType::kOthers; } +// static bool CrossOriginReadBlocking::IsBlockableScheme(const GURL& url) { // We exclude ftp:// from here. FTP doesn't provide a Content-Type // header which our policy depends on, so we cannot protect any @@ -372,6 +370,7 @@ bool CrossOriginReadBlocking::IsBlockableScheme(const GURL& url) { return url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme); } +// static bool CrossOriginReadBlocking::IsValidCorsHeaderSet( const url::Origin& frame_origin, const std::string& access_control_origin) { @@ -395,6 +394,7 @@ bool CrossOriginReadBlocking::IsValidCorsHeaderSet( url::Origin::Create(GURL(access_control_origin))); } +// static // This function is a slight modification of |net::SniffForHTML|. SniffingResult CrossOriginReadBlocking::SniffForHTML(StringPiece data) { // The content sniffers used by Chrome and Firefox are using "<!--" as one of @@ -446,6 +446,7 @@ SniffingResult CrossOriginReadBlocking::SniffForHTML(StringPiece data) { return kMaybe; } +// static SniffingResult CrossOriginReadBlocking::SniffForXML(base::StringPiece data) { // TODO(dsjang): Once CrossOriginReadBlocking is moved into the browser // process, we should do single-thread checking here for the static @@ -456,6 +457,7 @@ SniffingResult CrossOriginReadBlocking::SniffForXML(base::StringPiece data) { base::CompareCase::SENSITIVE); } +// static SniffingResult CrossOriginReadBlocking::SniffForJSON(base::StringPiece data) { // Currently this function looks for an opening brace ('{'), followed by a // double-quoted string literal, followed by a colon. Importantly, such a @@ -518,6 +520,7 @@ SniffingResult CrossOriginReadBlocking::SniffForJSON(base::StringPiece data) { return kMaybe; } +// static SniffingResult CrossOriginReadBlocking::SniffForFetchOnlyResource( base::StringPiece data) { // kScriptBreakingPrefixes contains prefixes that are conventionally used to @@ -659,7 +662,7 @@ CrossOriginReadBlocking::ResponseAnalyzer::ResponseAnalyzer( const GURL& request_url, const base::Optional<url::Origin>& request_initiator, const network::mojom::URLResponseHead& response, - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, mojom::RequestMode request_mode, const base::Optional<url::Origin>& isolated_world_origin, mojom::NetworkServiceClient* network_service_client) @@ -696,7 +699,7 @@ CrossOriginReadBlocking::ResponseAnalyzer::ResponseAnalyzer( should_block_based_on_headers_ = ShouldBlockBasedOnHeaders( request_mode, request_url, request_initiator, response, - request_initiator_site_lock, canonical_mime_type_, + request_initiator_origin_lock, canonical_mime_type_, &is_cors_blocking_expected_); // Check if the response seems sensitive and if so include in our CORB @@ -744,7 +747,7 @@ CrossOriginReadBlocking::ResponseAnalyzer::ShouldBlockBasedOnHeaders( const GURL& request_url, const base::Optional<url::Origin>& request_initiator, const network::mojom::URLResponseHead& response, - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, MimeType canonical_mime_type, bool* is_cors_blocking_expected) { if (is_cors_blocking_expected) @@ -760,7 +763,7 @@ CrossOriginReadBlocking::ResponseAnalyzer::ShouldBlockBasedOnHeaders( // unique origin makes CORB treat the response as cross-origin and thus // considers it eligible for blocking (based on content-type, sniffing, etc.). url::Origin initiator = - GetTrustworthyInitiator(request_initiator_site_lock, request_initiator); + GetTrustworthyInitiator(request_initiator_origin_lock, request_initiator); // Don't block same-origin documents. if (initiator.IsSameOriginWith(target_origin)) @@ -842,13 +845,13 @@ CrossOriginReadBlocking::ResponseAnalyzer::ShouldBlockBasedOnHeaders( // block will no longer be necessary since all failed CORS requests will be // blocked before reaching the renderer process (even without CORB's help). // Of course this assumes that OOR-CORS will use trustworthy - // |request_initiator| (i.e. vetted against |request_initiator|site_lock|). + // |request_initiator| (i.e. vetted against |request_initiator_origin_lock|). constexpr mojom::RequestMode kOverreachingRequestMode = mojom::RequestMode::kNoCors; // COEP is not supported when OOR-CORS is disabled. if (CrossOriginResourcePolicy::IsBlocked( request_url, request_url, request_initiator, response, - kOverreachingRequestMode, request_initiator_site_lock, + kOverreachingRequestMode, request_initiator_origin_lock, CrossOriginEmbedderPolicy())) { // Ignore mime types and/or sniffing and have CORB block all responses with // COR*P* header. @@ -1333,22 +1336,4 @@ void CrossOriginReadBlocking::ResponseAnalyzer::LogSensitiveResponseProtection( supports_range_requests_); } -// static -void CrossOriginReadBlocking::AddExceptionForPlugin(int process_id) { - std::set<int>& plugin_proxies = GetPluginProxyingProcesses(); - plugin_proxies.insert(process_id); -} - -// static -bool CrossOriginReadBlocking::ShouldAllowForPlugin(int process_id) { - std::set<int>& plugin_proxies = GetPluginProxyingProcesses(); - return base::Contains(plugin_proxies, process_id); -} - -// static -void CrossOriginReadBlocking::RemoveExceptionForPlugin(int process_id) { - std::set<int>& plugin_proxies = GetPluginProxyingProcesses(); - plugin_proxies.erase(process_id); -} - } // namespace network diff --git a/chromium/services/network/cross_origin_read_blocking.h b/chromium/services/network/public/cpp/cross_origin_read_blocking.h index 6538b0ff89f..5dcdd0e4579 100644 --- a/chromium/services/network/cross_origin_read_blocking.h +++ b/chromium/services/network/public/cpp/cross_origin_read_blocking.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SERVICES_NETWORK_CROSS_ORIGIN_READ_BLOCKING_H_ -#define SERVICES_NETWORK_CROSS_ORIGIN_READ_BLOCKING_H_ +#ifndef SERVICES_NETWORK_PUBLIC_CPP_CROSS_ORIGIN_READ_BLOCKING_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_CROSS_ORIGIN_READ_BLOCKING_H_ #include <memory> #include <string> @@ -37,7 +37,7 @@ class NetworkServiceClient; // steal private information from other sites. For more details see // services/network/cross_origin_read_blocking_explainer.md -class COMPONENT_EXPORT(NETWORK_SERVICE) CrossOriginReadBlocking { +class COMPONENT_EXPORT(NETWORK_CPP) CrossOriginReadBlocking { public: // This enum describes how CORB should decide whether to block a given // no-cors, cross-origin response. @@ -73,7 +73,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CrossOriginReadBlocking { // An instance for tracking the state of analyzing a single response // and deciding whether CORB should block the response. - class COMPONENT_EXPORT(NETWORK_SERVICE) ResponseAnalyzer { + class COMPONENT_EXPORT(NETWORK_CPP) ResponseAnalyzer { public: // Categorizes the resource MIME type for CORB protection logging. enum MimeTypeBucket { @@ -115,7 +115,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CrossOriginReadBlocking { const GURL& request_url, const base::Optional<url::Origin>& request_initiator, const network::mojom::URLResponseHead& response, - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, mojom::RequestMode request_mode, const base::Optional<url::Origin>& isolated_world_origin, mojom::NetworkServiceClient* network_service_client); @@ -197,7 +197,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CrossOriginReadBlocking { const GURL& request_url, const base::Optional<url::Origin>& request_initiator, const network::mojom::URLResponseHead& response, - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, MimeType canonical_mime_type, bool* is_cors_blocking_expected); @@ -334,23 +334,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CrossOriginReadBlocking { kYes, }; - // Notifies CORB that |process_id| is proxying requests on behalf of a - // universal-access plugin and therefore CORB should stop blocking requests - // marked as ResourceType::kPluginResource. - // - // TODO(lukasza, laforge): https://crbug.com/702995: Remove the static - // ...ForPlugin methods once Flash support is removed from Chromium (probably - // around 2020 - see https://www.chromium.org/flash-roadmap). - static void AddExceptionForPlugin(int process_id); - - // Returns true if CORB should ignore a request initiated by a universal - // access plugin - i.e. if |process_id| has been previously passed to - // AddExceptionForPlugin. - static bool ShouldAllowForPlugin(int process_id); - - // Reverts AddExceptionForPlugin. - static void RemoveExceptionForPlugin(int process_id); - private: CrossOriginReadBlocking(); // Not instantiable. @@ -406,4 +389,4 @@ inline std::ostream& operator<<( } // namespace network -#endif // SERVICES_NETWORK_CROSS_ORIGIN_READ_BLOCKING_H_ +#endif // SERVICES_NETWORK_PUBLIC_CPP_CROSS_ORIGIN_READ_BLOCKING_H_ diff --git a/chromium/services/network/cross_origin_read_blocking_unittest.cc b/chromium/services/network/public/cpp/cross_origin_read_blocking_unittest.cc index 5fd359aec28..d7638788cb5 100644 --- a/chromium/services/network/cross_origin_read_blocking_unittest.cc +++ b/chromium/services/network/public/cpp/cross_origin_read_blocking_unittest.cc @@ -20,7 +20,7 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_test_util.h" -#include "services/network/cross_origin_read_blocking.h" +#include "services/network/public/cpp/cross_origin_read_blocking.h" #include "services/network/public/mojom/url_response_head.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -2028,15 +2028,15 @@ class ResponseAnalyzerTest : public testing::Test, // Create the site lock, which may differ from the initiator origin or be // empty. - base::Optional<url::Origin> request_initiator_site_lock; + base::Optional<url::Origin> request_initiator_origin_lock; if (strlen(scenario.initiator_site_lock) > 0) - request_initiator_site_lock = + request_initiator_origin_lock = url::Origin::Create(GURL(scenario.initiator_site_lock)); // Create a ResponseAnalyzer to test. analyzer_ = std::make_unique<ResponseAnalyzer>( request->url(), request->initiator(), response, - request_initiator_site_lock, request_mode, + request_initiator_origin_lock, request_mode, base::nullopt /* isolated_world_origin */, nullptr /* network_service_client */); diff --git a/chromium/services/network/public/cpp/cross_origin_resource_policy.cc b/chromium/services/network/public/cpp/cross_origin_resource_policy.cc index 52fbbfe08c8..cd2b23c6905 100644 --- a/chromium/services/network/public/cpp/cross_origin_resource_policy.cc +++ b/chromium/services/network/public/cpp/cross_origin_resource_policy.cc @@ -143,7 +143,7 @@ base::Optional<mojom::BlockedByResponseReason> IsBlockedInternal( const GURL& request_url, const base::Optional<url::Origin>& request_initiator, mojom::RequestMode request_mode, - base::Optional<url::Origin> request_initiator_site_lock, + base::Optional<url::Origin> request_initiator_origin_lock, mojom::CrossOriginEmbedderPolicyValue embedder_policy) { // COEP https://mikewest.github.io/corpp/#corp-check bool upgrade_to_same_origin = false; @@ -172,7 +172,7 @@ base::Optional<mojom::BlockedByResponseReason> IsBlockedInternal( // origin, then return allowed. url::Origin target_origin = url::Origin::Create(request_url); url::Origin initiator = - GetTrustworthyInitiator(request_initiator_site_lock, request_initiator); + GetTrustworthyInitiator(request_initiator_origin_lock, request_initiator); if (initiator == target_origin) return base::nullopt; @@ -207,7 +207,7 @@ base::Optional<mojom::BlockedByResponseReason> IsBlockedInternalWithReporting( const GURL& original_url, const base::Optional<url::Origin>& request_initiator, mojom::RequestMode request_mode, - base::Optional<url::Origin> request_initiator_site_lock, + base::Optional<url::Origin> request_initiator_origin_lock, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter) { constexpr auto kBlockedDueToCoep = mojom::BlockedByResponseReason:: @@ -217,7 +217,7 @@ base::Optional<mojom::BlockedByResponseReason> IsBlockedInternalWithReporting( reporter) { const auto result = IsBlockedInternal( policy, request_url, request_initiator, request_mode, - request_initiator_site_lock, embedder_policy.report_only_value); + request_initiator_origin_lock, embedder_policy.report_only_value); UMA_HISTOGRAM_ENUMERATION( "NetworkService.CrossOriginResourcePolicy.ReportOnlyResult", ToCorpResult(result)); @@ -234,7 +234,7 @@ base::Optional<mojom::BlockedByResponseReason> IsBlockedInternalWithReporting( const auto result = IsBlockedInternal(policy, request_url, request_initiator, request_mode, - request_initiator_site_lock, embedder_policy.value); + request_initiator_origin_lock, embedder_policy.value); UMA_HISTOGRAM_ENUMERATION("NetworkService.CrossOriginResourcePolicy.Result", ToCorpResult(result)); if (reporter && @@ -259,7 +259,7 @@ CrossOriginResourcePolicy::IsBlocked( const base::Optional<url::Origin>& request_initiator, const network::mojom::URLResponseHead& response, mojom::RequestMode request_mode, - base::Optional<url::Origin> request_initiator_site_lock, + base::Optional<url::Origin> request_initiator_origin_lock, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter) { // From https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header: @@ -279,7 +279,7 @@ CrossOriginResourcePolicy::IsBlocked( return IsBlockedInternalWithReporting( policy, request_url, original_url, request_initiator, request_mode, - request_initiator_site_lock, embedder_policy, reporter); + request_initiator_origin_lock, embedder_policy, reporter); } // static @@ -290,7 +290,7 @@ CrossOriginResourcePolicy::IsBlockedByHeaderValue( const base::Optional<url::Origin>& request_initiator, base::Optional<std::string> corp_header_value, mojom::RequestMode request_mode, - base::Optional<url::Origin> request_initiator_site_lock, + base::Optional<url::Origin> request_initiator_origin_lock, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter) { // From https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header: @@ -302,7 +302,7 @@ CrossOriginResourcePolicy::IsBlockedByHeaderValue( return IsBlockedInternalWithReporting( policy, request_url, original_url, request_initiator, request_mode, - request_initiator_site_lock, embedder_policy, reporter); + request_initiator_origin_lock, embedder_policy, reporter); } // static @@ -312,7 +312,7 @@ CrossOriginResourcePolicy::IsNavigationBlocked( const GURL& original_url, const base::Optional<url::Origin>& request_initiator, const network::mojom::URLResponseHead& response, - base::Optional<url::Origin> request_initiator_site_lock, + base::Optional<url::Origin> request_initiator_origin_lock, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter) { ParsedHeader policy = @@ -320,7 +320,7 @@ CrossOriginResourcePolicy::IsNavigationBlocked( return IsBlockedInternalWithReporting( policy, request_url, original_url, request_initiator, - mojom::RequestMode::kNavigate, request_initiator_site_lock, + mojom::RequestMode::kNavigate, request_initiator_origin_lock, embedder_policy, reporter); } diff --git a/chromium/services/network/public/cpp/cross_origin_resource_policy.h b/chromium/services/network/public/cpp/cross_origin_resource_policy.h index f1ad4c3bcf6..0d7829faf1f 100644 --- a/chromium/services/network/public/cpp/cross_origin_resource_policy.h +++ b/chromium/services/network/public/cpp/cross_origin_resource_policy.h @@ -45,7 +45,7 @@ class COMPONENT_EXPORT(NETWORK_CPP) CrossOriginResourcePolicy { const base::Optional<url::Origin>& request_initiator, const network::mojom::URLResponseHead& response, mojom::RequestMode request_mode, - base::Optional<url::Origin> request_initiator_site_lock, + base::Optional<url::Origin> request_initiator_origin_lock, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter = nullptr) WARN_UNUSED_RESULT; @@ -58,7 +58,7 @@ class COMPONENT_EXPORT(NETWORK_CPP) CrossOriginResourcePolicy { const base::Optional<url::Origin>& request_initiator, base::Optional<std::string> corp_header_value, mojom::RequestMode request_mode, - base::Optional<url::Origin> request_initiator_site_lock, + base::Optional<url::Origin> request_initiator_origin_lock, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter = nullptr) WARN_UNUSED_RESULT; @@ -70,7 +70,7 @@ class COMPONENT_EXPORT(NETWORK_CPP) CrossOriginResourcePolicy { const GURL& original_url, const base::Optional<url::Origin>& request_initiator, const network::mojom::URLResponseHead& response, - base::Optional<url::Origin> request_initiator_site_lock, + base::Optional<url::Origin> request_initiator_origin_lock, const CrossOriginEmbedderPolicy& embedder_policy, mojom::CrossOriginEmbedderPolicyReporter* reporter = nullptr); diff --git a/chromium/services/network/public/cpp/data_element.cc b/chromium/services/network/public/cpp/data_element.cc index 9a959842e9d..4a09cb877c6 100644 --- a/chromium/services/network/public/cpp/data_element.cc +++ b/chromium/services/network/public/cpp/data_element.cc @@ -77,6 +77,13 @@ void DataElement::SetToChunkedDataPipe( chunked_data_pipe_getter_ = std::move(chunked_data_pipe_getter); } +void DataElement::SetToReadOnceStream( + mojo::PendingRemote<mojom::ChunkedDataPipeGetter> + chunked_data_pipe_getter) { + type_ = mojom::DataElementType::kReadOnceStream; + chunked_data_pipe_getter_ = std::move(chunked_data_pipe_getter); +} + base::File DataElement::ReleaseFile() { return std::move(file_); } @@ -103,7 +110,9 @@ mojo::PendingRemote<mojom::DataPipeGetter> DataElement::CloneDataPipeGetter() mojo::PendingRemote<mojom::ChunkedDataPipeGetter> DataElement::ReleaseChunkedDataPipeGetter() { - DCHECK_EQ(mojom::DataElementType::kChunkedDataPipe, type_); + DCHECK(type_ == mojom::DataElementType::kChunkedDataPipe || + type_ == mojom::DataElementType::kReadOnceStream) + << type_; return std::move(chunked_data_pipe_getter_); } @@ -138,6 +147,9 @@ void PrintTo(const DataElement& x, std::ostream* os) { case mojom::DataElementType::kChunkedDataPipe: *os << "TYPE_CHUNKED_DATA_PIPE"; break; + case mojom::DataElementType::kReadOnceStream: + *os << "TYPE_READ_ONCE_STREAM"; + break; case mojom::DataElementType::kUnknown: *os << "TYPE_UNKNOWN"; break; @@ -164,6 +176,8 @@ bool operator==(const DataElement& a, const DataElement& b) { return false; case mojom::DataElementType::kChunkedDataPipe: return false; + case mojom::DataElementType::kReadOnceStream: + return false; case mojom::DataElementType::kUnknown: NOTREACHED(); return false; diff --git a/chromium/services/network/public/cpp/data_element.h b/chromium/services/network/public/cpp/data_element.h index 26d0f4fc4fe..ed2eed4a3d8 100644 --- a/chromium/services/network/public/cpp/data_element.h +++ b/chromium/services/network/public/cpp/data_element.h @@ -169,6 +169,10 @@ class COMPONENT_EXPORT(NETWORK_CPP_BASE) DataElement { // server known to support chunked uploads. void SetToChunkedDataPipe(mojo::PendingRemote<mojom::ChunkedDataPipeGetter> chunked_data_pipe_getter); + // Almost same as above except |chunked_data_pipe_getter| is read only once + // and you must talk with a server supporting chunked upload. + void SetToReadOnceStream(mojo::PendingRemote<mojom::ChunkedDataPipeGetter> + chunked_data_pipe_getter); // Takes ownership of the File, if this is of TYPE_RAW_FILE. The file is open // for reading (asynchronous reading on Windows). diff --git a/chromium/services/network/public/cpp/empty_url_loader_client.cc b/chromium/services/network/public/cpp/empty_url_loader_client.cc index 6d869d05675..ded2ca53f31 100644 --- a/chromium/services/network/public/cpp/empty_url_loader_client.cc +++ b/chromium/services/network/public/cpp/empty_url_loader_client.cc @@ -26,7 +26,7 @@ EmptyURLLoaderClientWrapper::EmptyURLLoaderClientWrapper( mojo::PendingRemote<mojom::URLLoader> url_loader) : receiver_(&client_, std::move(receiver)), url_loader_(std::move(url_loader)) { - client_.Drain(base::BindOnce(&EmptyURLLoaderClientWrapper::DeleteSelf, + client_.Drain(base::BindOnce(&EmptyURLLoaderClientWrapper::DidDrain, base::Unretained(this))); receiver_.set_disconnect_handler(base::BindOnce( &EmptyURLLoaderClientWrapper::DeleteSelf, base::Unretained(this))); @@ -34,6 +34,11 @@ EmptyURLLoaderClientWrapper::EmptyURLLoaderClientWrapper( EmptyURLLoaderClientWrapper::~EmptyURLLoaderClientWrapper() = default; +void EmptyURLLoaderClientWrapper::DidDrain( + const network::URLLoaderCompletionStatus& status) { + DeleteSelf(); +} + void EmptyURLLoaderClientWrapper::DeleteSelf() { delete this; } @@ -41,15 +46,16 @@ void EmptyURLLoaderClientWrapper::DeleteSelf() { EmptyURLLoaderClient::EmptyURLLoaderClient() = default; EmptyURLLoaderClient::~EmptyURLLoaderClient() = default; -void EmptyURLLoaderClient::Drain(base::OnceClosure callback) { +void EmptyURLLoaderClient::Drain( + base::OnceCallback<void(const URLLoaderCompletionStatus&)> callback) { DCHECK(!callback_); callback_ = std::move(callback); MaybeDone(); } void EmptyURLLoaderClient::MaybeDone() { - if (done_ && callback_) - std::move(callback_).Run(); + if (done_status_ && callback_) + std::move(callback_).Run(*done_status_); } void EmptyURLLoaderClient::OnReceiveResponse( @@ -77,7 +83,7 @@ void EmptyURLLoaderClient::OnStartLoadingResponseBody( } void EmptyURLLoaderClient::OnComplete(const URLLoaderCompletionStatus& status) { - done_ = true; + done_status_ = status; MaybeDone(); } diff --git a/chromium/services/network/public/cpp/empty_url_loader_client.h b/chromium/services/network/public/cpp/empty_url_loader_client.h index bcff77f086d..5a0947260c7 100644 --- a/chromium/services/network/public/cpp/empty_url_loader_client.h +++ b/chromium/services/network/public/cpp/empty_url_loader_client.h @@ -14,6 +14,7 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/system/data_pipe_drainer.h" +#include "services/network/public/cpp/url_loader_completion_status.h" #include "services/network/public/mojom/url_loader.mojom.h" namespace network { @@ -27,7 +28,7 @@ class COMPONENT_EXPORT(NETWORK_CPP) EmptyURLLoaderClient ~EmptyURLLoaderClient() override; // Calls |callback| when the request is done. - void Drain(base::OnceClosure callback); + void Drain(base::OnceCallback<void(const URLLoaderCompletionStatus&)>); private: void MaybeDone(); @@ -51,8 +52,8 @@ class COMPONENT_EXPORT(NETWORK_CPP) EmptyURLLoaderClient std::unique_ptr<mojo::DataPipeDrainer> response_body_drainer_; - bool done_ = false; - base::OnceClosure callback_; + base::Optional<URLLoaderCompletionStatus> done_status_; + base::OnceCallback<void(const URLLoaderCompletionStatus&)> callback_; DISALLOW_COPY_AND_ASSIGN(EmptyURLLoaderClient); }; @@ -75,6 +76,7 @@ class COMPONENT_EXPORT(NETWORK_CPP) EmptyURLLoaderClientWrapper { mojo::PendingReceiver<mojom::URLLoaderClient> receiver, mojo::PendingRemote<mojom::URLLoader> url_loader); + void DidDrain(const network::URLLoaderCompletionStatus& status); void DeleteSelf(); EmptyURLLoaderClient client_; diff --git a/chromium/services/network/public/cpp/features.cc b/chromium/services/network/public/cpp/features.cc index 7ea2ac326c8..422dac7aba9 100644 --- a/chromium/services/network/public/cpp/features.cc +++ b/chromium/services/network/public/cpp/features.cc @@ -56,7 +56,7 @@ const base::Feature kDelayRequestsOnMultiplexedConnections{ // When kRequestInitiatorSiteLock is enabled, then CORB, CORP and Sec-Fetch-Site // will validate network::ResourceRequest::request_initiator against -// network::mojom::URLLoaderFactoryParams::request_initiator_site_lock. +// network::mojom::URLLoaderFactoryParams::request_initiator_origin_lock. const base::Feature kRequestInitiatorSiteLock{"RequestInitiatorSiteLock", base::FEATURE_ENABLED_BY_DEFAULT}; @@ -84,17 +84,15 @@ const base::Feature kProactivelyThrottleLowPriorityRequests{ // Enables Cross-Origin Opener Policy (COOP). // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e -// Currently this feature is enabled for all platforms except WebView. It is not -// possible to distinguish between Android and WebView here, so we enable the -// feature on Android via finch. -const base::Feature kCrossOriginOpenerPolicy { - "CrossOriginOpenerPolicy", -#if defined(OS_ANDROID) - base::FEATURE_DISABLED_BY_DEFAULT -#else - base::FEATURE_ENABLED_BY_DEFAULT -#endif -}; +// Currently this feature is enabled for all platforms except WebView. +const base::Feature kCrossOriginOpenerPolicy{"CrossOriginOpenerPolicy", + base::FEATURE_ENABLED_BY_DEFAULT}; + +// Enables Cross-Origin-Opener-Policy reporting API origin trial. It will be +// used as a kill switch during the experiment. +const base::Feature kCrossOriginOpenerPolicyReportingOriginTrial{ + "CrossOriginOpenerPolicyReportingOriginTrial", + base::FEATURE_ENABLED_BY_DEFAULT}; // Enables Cross-Origin Opener Policy (COOP) reporting. // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e @@ -104,8 +102,7 @@ const base::Feature kCrossOriginOpenerPolicyReporting{ // Enables Cross-Origin Opener Policy (COOP) access reporting. // https://github.com/camillelamy/explainers/blob/master/coop_reporting.md#report-blocked-accesses-to-other-windows const base::Feature kCrossOriginOpenerPolicyAccessReporting{ - "CrossOriginOpenerPolicyAccessReporting", - base::FEATURE_DISABLED_BY_DEFAULT}; + "CrossOriginOpenerPolicyAccessReporting", base::FEATURE_ENABLED_BY_DEFAULT}; // Enables Cross-Origin Embedder Policy (COEP). // https://github.com/mikewest/corpp @@ -113,13 +110,18 @@ const base::Feature kCrossOriginOpenerPolicyAccessReporting{ const base::Feature kCrossOriginEmbedderPolicy{ "CrossOriginEmbedderPolicy", base::FEATURE_ENABLED_BY_DEFAULT}; -// When kBlockNonSecureExternalRequests is enabled, requests initiated from a -// pubic network may only target a private network if the initiating context -// is secure. +// Enables the most recent developments on the crossOriginIsolated property. +// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/crossOriginIsolated +const base::Feature kCrossOriginIsolated{"CrossOriginIsolated", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// When kBlockInsecurePrivateNetworkRequests is enabled, requests initiated +// from a less-private network may only target a more-private network if the +// initiating context is secure. // // https://wicg.github.io/cors-rfc1918/#integration-fetch -const base::Feature kBlockNonSecureExternalRequests{ - "BlockNonSecureExternalRequests", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kBlockInsecurePrivateNetworkRequests{ + "BlockInsecurePrivateNetworkRequests", base::FEATURE_DISABLED_BY_DEFAULT}; // Enables or defaults splittup up server (not proxy) entries in the // HttpAuthCache. @@ -129,7 +131,7 @@ const base::Feature kSplitAuthCacheByNetworkIsolationKey{ // Enable usage of hardcoded DoH upgrade mapping for use in automatic mode. const base::Feature kDnsOverHttpsUpgrade { "DnsOverHttpsUpgrade", -#if defined(OS_CHROMEOS) || defined(OS_MACOSX) || defined(OS_ANDROID) || \ +#if defined(OS_CHROMEOS) || defined(OS_MAC) || defined(OS_ANDROID) || \ defined(OS_WIN) base::FEATURE_ENABLED_BY_DEFAULT #else @@ -183,7 +185,7 @@ const char kCorbAllowlistAlsoAppliesToOorCorsParamName[] = "AllowlistForCorbAndCors"; // Controls whether a |request_initiator| that mismatches -// |request_initiator_site_lock| leads to 1) failing the HTTP request and 2) +// |request_initiator_origin_lock| leads to 1) failing the HTTP request and 2) // calling mojo::ReportBadMessage (on desktop platforms, where NetworkService // is hosted outside of the Browser process, this leads to DumpWithoutCrashing // and does *not* lead to a renderer kill). @@ -253,5 +255,12 @@ bool ShouldEnableOutOfBlinkCorsForTesting() { const base::Feature kWebSocketReassembleShortMessages{ "WebSocketReassembleShortMessages", base::FEATURE_ENABLED_BY_DEFAULT}; +// Controls whether SCT audit reports are queued and the rate at which they +// should be sampled. +const base::Feature kSCTAuditing{"SCTAuditing", + base::FEATURE_DISABLED_BY_DEFAULT}; +constexpr base::FeatureParam<double> kSCTAuditingSamplingRate{ + &kSCTAuditing, "sampling_rate", 0.0}; + } // namespace features } // namespace network diff --git a/chromium/services/network/public/cpp/features.h b/chromium/services/network/public/cpp/features.h index 213184d4cb4..92db9c96dde 100644 --- a/chromium/services/network/public/cpp/features.h +++ b/chromium/services/network/public/cpp/features.h @@ -39,11 +39,15 @@ extern const base::Feature kCrossOriginOpenerPolicy; COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kCrossOriginOpenerPolicyReporting; COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kCrossOriginOpenerPolicyReportingOriginTrial; +COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kCrossOriginOpenerPolicyAccessReporting; COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kCrossOriginEmbedderPolicy; COMPONENT_EXPORT(NETWORK_CPP) -extern const base::Feature kBlockNonSecureExternalRequests; +extern const base::Feature kCrossOriginIsolated; +COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kBlockInsecurePrivateNetworkRequests; COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kSplitAuthCacheByNetworkIsolationKey; COMPONENT_EXPORT(NETWORK_CPP) @@ -98,6 +102,11 @@ bool ShouldEnableOutOfBlinkCorsForTesting(); COMPONENT_EXPORT(NETWORK_CPP) extern const base::Feature kWebSocketReassembleShortMessages; +COMPONENT_EXPORT(NETWORK_CPP) +extern const base::Feature kSCTAuditing; +COMPONENT_EXPORT(NETWORK_CPP) +extern const base::FeatureParam<double> kSCTAuditingSamplingRate; + } // namespace features } // namespace network diff --git a/chromium/services/network/public/cpp/host_resolver_mojom_traits.cc b/chromium/services/network/public/cpp/host_resolver_mojom_traits.cc index 9832b21aacd..c2126c1d7f4 100644 --- a/chromium/services/network/public/cpp/host_resolver_mojom_traits.cc +++ b/chromium/services/network/public/cpp/host_resolver_mojom_traits.cc @@ -164,13 +164,6 @@ StructTraits<DnsConfigOverridesDataView, net::DnsConfigOverrides>:: // static DnsConfigOverrides::Tristate -StructTraits<DnsConfigOverridesDataView, net::DnsConfigOverrides>:: - randomize_ports(const net::DnsConfigOverrides& overrides) { - return ToTristate(overrides.randomize_ports); -} - -// static -DnsConfigOverrides::Tristate StructTraits<DnsConfigOverridesDataView, net::DnsConfigOverrides>::rotate( const net::DnsConfigOverrides& overrides) { return ToTristate(overrides.rotate); @@ -230,7 +223,6 @@ bool StructTraits<DnsConfigOverridesDataView, net::DnsConfigOverrides>::Read( out->append_to_multi_label_name = FromTristate(data.append_to_multi_label_name()); - out->randomize_ports = FromTristate(data.randomize_ports()); if (data.ndots() < -1) return false; diff --git a/chromium/services/network/public/cpp/host_resolver_mojom_traits.h b/chromium/services/network/public/cpp/host_resolver_mojom_traits.h index cae8e0efdff..95910576110 100644 --- a/chromium/services/network/public/cpp/host_resolver_mojom_traits.h +++ b/chromium/services/network/public/cpp/host_resolver_mojom_traits.h @@ -51,8 +51,6 @@ struct StructTraits<network::mojom::DnsConfigOverridesDataView, static network::mojom::DnsConfigOverrides_Tristate append_to_multi_label_name( const net::DnsConfigOverrides& overrides); - static network::mojom::DnsConfigOverrides_Tristate randomize_ports( - const net::DnsConfigOverrides& overrides); static int ndots(const net::DnsConfigOverrides& overrides) { return overrides.ndots.value_or(-1); diff --git a/chromium/services/network/public/cpp/host_resolver_mojom_traits_unittest.cc b/chromium/services/network/public/cpp/host_resolver_mojom_traits_unittest.cc index 9a5dacd8e88..448f3846bde 100644 --- a/chromium/services/network/public/cpp/host_resolver_mojom_traits_unittest.cc +++ b/chromium/services/network/public/cpp/host_resolver_mojom_traits_unittest.cc @@ -37,7 +37,6 @@ TEST(HostResolverMojomTraitsTest, DnsConfigOverridesRoundtrip_FullySpecified) { std::make_pair(net::DnsHostsKey("host2", net::ADDRESS_FAMILY_IPV4), net::IPAddress(2, 3, 4, 5))}); original.append_to_multi_label_name = true; - original.randomize_ports = false; original.ndots = 2; original.timeout = base::TimeDelta::FromHours(4); original.attempts = 1; diff --git a/chromium/services/network/public/cpp/initiator_lock_compatibility.cc b/chromium/services/network/public/cpp/initiator_lock_compatibility.cc index 136424d1ef2..0068639a1ff 100644 --- a/chromium/services/network/public/cpp/initiator_lock_compatibility.cc +++ b/chromium/services/network/public/cpp/initiator_lock_compatibility.cc @@ -20,49 +20,34 @@ namespace network { InitiatorLockCompatibility VerifyRequestInitiatorLock( - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, const base::Optional<url::Origin>& request_initiator) { - if (!request_initiator_site_lock.has_value()) + if (!request_initiator_origin_lock.has_value()) return InitiatorLockCompatibility::kNoLock; - const url::Origin& lock = request_initiator_site_lock.value(); + const url::Origin& lock = request_initiator_origin_lock.value(); if (!request_initiator.has_value()) return InitiatorLockCompatibility::kNoInitiator; const url::Origin& initiator = request_initiator.value(); - // TODO(lukasza, nasko): Also consider equality of precursor origins (e.g. if - // |initiator| is opaque, then it's precursor origin should match the |lock| - // [or |lock|'s precursor if |lock| is also opaque]). - if (initiator.opaque() || (initiator == lock)) + if (initiator == lock) return InitiatorLockCompatibility::kCompatibleLock; - // TODO(lukasza): https://crbug.com/891872: Return kIncorrectLock if - // the origins do not match exactly in the previous if statement. This should - // be possible to do once we no longer vend process-wide factory. - if (!initiator.opaque() && !lock.opaque() && - initiator.scheme() == lock.scheme() && - initiator.GetURL().SchemeIsHTTPOrHTTPS()) { - if (initiator.GetURL().HostIsIPAddress()) { - // For IP addresses, we require host equality (allowing ports to differ, - // since site_url ignores ports). See also https://crbug.com/1051674. - if (initiator.host() == lock.host()) - return InitiatorLockCompatibility::kCompatibleLock; - } else { - // For non-IP-address origins, we require sites (eTLD+1) to match - // (again ignoring ports). - std::string lock_domain = lock.host(); - if (!lock_domain.empty() && lock_domain.back() == '.') - lock_domain.erase(lock_domain.length() - 1); - if (initiator.DomainIs(lock_domain)) - return InitiatorLockCompatibility::kCompatibleLock; - } + // Opaque |initiator| is always allowed. In particular, a factory locked to a + // non-opaque |lock| may be used by an opaque |initiator| - for example when + // the factory is inherited by a data: URL frame. + if (initiator.opaque()) { + // TODO(lukasza, nasko): Also consider equality of precursor origins (e.g. + // if |initiator| is opaque, then it's precursor origin should match the + // |lock| [or |lock|'s precursor if |lock| is also opaque]). + return InitiatorLockCompatibility::kCompatibleLock; } return InitiatorLockCompatibility::kIncorrectLock; } url::Origin GetTrustworthyInitiator( - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, const base::Optional<url::Origin>& request_initiator) { // Returning a unique origin as a fallback should be safe - such origin will // be considered cross-origin from all other origins. @@ -75,7 +60,7 @@ url::Origin GetTrustworthyInitiator( return request_initiator.value(); InitiatorLockCompatibility initiator_compatibility = - VerifyRequestInitiatorLock(request_initiator_site_lock, + VerifyRequestInitiatorLock(request_initiator_origin_lock, request_initiator); if (initiator_compatibility == InitiatorLockCompatibility::kIncorrectLock) return unique_origin_fallback; diff --git a/chromium/services/network/public/cpp/initiator_lock_compatibility.h b/chromium/services/network/public/cpp/initiator_lock_compatibility.h index e521251585d..9275c998bf3 100644 --- a/chromium/services/network/public/cpp/initiator_lock_compatibility.h +++ b/chromium/services/network/public/cpp/initiator_lock_compatibility.h @@ -21,11 +21,11 @@ class URLLoaderFactoryParams; // tools/metrics/histograms/enums.xml. enum class InitiatorLockCompatibility { // Request came from a browser process and so the - // |request_initiator_site_lock| doesn't apply. + // |request_initiator_origin_lock| doesn't apply. kBrowserProcess = 0, - // |request_initiator_site_lock| is missing - see https://crbug.com/891872 - // and RenderProcessHostImpl::CreateURLLoaderFactoryForRendererProcess. + // |request_initiator_origin_lock| is missing. For historical context see + // https://crbug.com/1098938. kNoLock = 1, // |request_initiator| is missing. This indicates that the renderer has a bug @@ -33,18 +33,18 @@ enum class InitiatorLockCompatibility { kNoInitiator = 2, // |request.request_initiator| is compatible with - // |factory_params_.request_initiator_site_lock| - either + // |factory_params_.request_initiator_origin_lock| - either // |request.request_initiator| is opaque or it is equal to - // |request_initiator_site_lock|. + // |request_initiator_origin_lock|. kCompatibleLock = 3, // |request.request_initiator| is incompatible with - // |factory_params_.request_initiator_site_lock|. Cases known so far where + // |factory_params_.request_initiator_origin_lock|. Cases known so far where // this can occur: // - HTML Imports (see https://crbug.com/871827#c9). kIncorrectLock = 4, - // Covered by CrossOriginReadBlocking::ShouldAllowForPlugin. + // Covered by CrossOriginReadBlockingExceptionForPlugin::ShouldAllowForPlugin. kExcludedCorbForPlugin = 6, // Covered by AddAllowedRequestInitiatorForPlugin. @@ -54,24 +54,24 @@ enum class InitiatorLockCompatibility { }; // Verifies if |request.request_initiator| matches -// |factory_params.request_initiator_site_lock|. +// |factory_params.request_initiator_origin_lock|. // // This should only be called for requests from renderer processes // (ones that are not coverd by the kExcludedPlugin exception). COMPONENT_EXPORT(NETWORK_CPP) InitiatorLockCompatibility VerifyRequestInitiatorLock( - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, const base::Optional<url::Origin>& request_initiator); // Gets initiator of request, falling back to a unique origin if // 1) |request_initiator| is missing or -// 2) |request_initiator| is incompatible with |request_initiator_site_lock|. +// 2) |request_initiator| is incompatible with |request_initiator_origin_lock|. // -// |request_initiator_site_lock| is the origin to which the URLLoaderFactory of -// the request is locked in a trustworthy way. +// |request_initiator_origin_lock| is the origin to which the URLLoaderFactory +// of the request is locked in a trustworthy way. // Example: -// URLLoaderFactoryParams::request_initiator_site_lock -// SubresourceSignedExchangeURLLoaderFactory::request_initiator_site_lock +// URLLoaderFactoryParams::request_initiator_origin_lock +// SubresourceSignedExchangeURLLoaderFactory::request_initiator_origin_lock // |request_initiator| should come from net::URLRequest::initiator() or // network::ResourceRequest::request_initiator which may be initially set in an // untrustworthy process (eg: renderer process). @@ -82,7 +82,7 @@ InitiatorLockCompatibility VerifyRequestInitiatorLock( // remove this, this header can be moved to non-public directory. COMPONENT_EXPORT(NETWORK_CPP) url::Origin GetTrustworthyInitiator( - const base::Optional<url::Origin>& request_initiator_site_lock, + const base::Optional<url::Origin>& request_initiator_origin_lock, const base::Optional<url::Origin>& request_initiator); } // namespace network diff --git a/chromium/services/network/public/cpp/initiator_lock_compatibility_unittest.cc b/chromium/services/network/public/cpp/initiator_lock_compatibility_unittest.cc index 552c0aaccf2..c65d77a43d4 100644 --- a/chromium/services/network/public/cpp/initiator_lock_compatibility_unittest.cc +++ b/chromium/services/network/public/cpp/initiator_lock_compatibility_unittest.cc @@ -12,7 +12,7 @@ namespace network { -TEST(InitiatorLockCompatibilityTest, VerifyRequestInitiatorSiteLock) { +TEST(InitiatorLockCompatibilityTest, VerifyRequestInitiatorOriginLock) { url::Origin opaque_origin = url::Origin(); url::Origin opaque_origin2 = url::Origin(); @@ -66,24 +66,19 @@ TEST(InitiatorLockCompatibilityTest, VerifyRequestInitiatorSiteLock) { EXPECT_EQ(InitiatorLockCompatibility::kIncorrectLock, VerifyRequestInitiatorLock(foo_example_com, bar_example_com)); - // Site-URL-based comparisons. - // - // TODO(lukasza): These should result in kIncorrectLock eventually (once - // request_initiator_site_lock becomes request_initiator_origin_lock - see - // https://crbug.com/891872. - EXPECT_EQ(InitiatorLockCompatibility::kCompatibleLock, + // Site != origin. + EXPECT_EQ(InitiatorLockCompatibility::kIncorrectLock, VerifyRequestInitiatorLock(example_com, bar_foo_example_com)); - EXPECT_EQ(InitiatorLockCompatibility::kCompatibleLock, + EXPECT_EQ(InitiatorLockCompatibility::kIncorrectLock, VerifyRequestInitiatorLock(foo_example_com, bar_foo_example_com)); EXPECT_EQ( - InitiatorLockCompatibility::kCompatibleLock, + InitiatorLockCompatibility::kIncorrectLock, VerifyRequestInitiatorLock(ip_origin1, ip_origin1_with_different_port)); - // The trailing dot is not important (at least for site-URL-based - // comparisons). - EXPECT_EQ(InitiatorLockCompatibility::kCompatibleLock, + // The trailing dot. + EXPECT_EQ(InitiatorLockCompatibility::kIncorrectLock, VerifyRequestInitiatorLock(foo_example_com_dot, foo_example_com)); - EXPECT_EQ(InitiatorLockCompatibility::kCompatibleLock, + EXPECT_EQ(InitiatorLockCompatibility::kIncorrectLock, VerifyRequestInitiatorLock(foo_example_com, foo_example_com_dot)); } diff --git a/chromium/services/network/public/cpp/ip_address_space_util.cc b/chromium/services/network/public/cpp/ip_address_space_util.cc new file mode 100644 index 00000000000..a9824b1f17f --- /dev/null +++ b/chromium/services/network/public/cpp/ip_address_space_util.cc @@ -0,0 +1,44 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/network/public/cpp/ip_address_space_util.h" + +#include "net/base/ip_address.h" + +namespace network { + +using mojom::IPAddressSpace; + +IPAddressSpace IPAddressToIPAddressSpace(const net::IPAddress& address) { + if (!address.IsValid()) { + return IPAddressSpace::kUnknown; + } + + if (address.IsLoopback()) { + return IPAddressSpace::kLocal; + } + + if (!address.IsPubliclyRoutable()) { + return IPAddressSpace::kPrivate; + } + + return IPAddressSpace::kPublic; +} + +// For comparison purposes, we treat kUnknown the same as kPublic. +IPAddressSpace CollapseUnknown(IPAddressSpace space) { + if (space == IPAddressSpace::kUnknown) { + return IPAddressSpace::kPublic; + } + return space; +} + +bool IsLessPublicAddressSpace(IPAddressSpace lhs, IPAddressSpace rhs) { + // Apart from the special case for kUnknown, the built-in comparison operator + // works just fine. The comment on IPAddressSpace's definition notes that the + // enum values' ordering matters. + return CollapseUnknown(lhs) < CollapseUnknown(rhs); +} + +} // namespace network diff --git a/chromium/services/network/public/cpp/ip_address_space_util.h b/chromium/services/network/public/cpp/ip_address_space_util.h new file mode 100644 index 00000000000..d16f8f981eb --- /dev/null +++ b/chromium/services/network/public/cpp/ip_address_space_util.h @@ -0,0 +1,41 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_NETWORK_PUBLIC_CPP_IP_ADDRESS_SPACE_UTIL_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_IP_ADDRESS_SPACE_UTIL_H_ + +#include "services/network/public/mojom/ip_address_space.mojom.h" + +namespace net { + +class IPAddress; + +} // namespace net + +namespace network { + +// Returns the IPAddressSpace from an IPAddress. +// +// This can be used as-is for subresource requests. For documents, also take +// into account the 'treat-as-public-address' CSP directive. +mojom::IPAddressSpace COMPONENT_EXPORT(NETWORK_CPP) + IPAddressToIPAddressSpace(const net::IPAddress& address); + +// Returns whether |lhs| is less public than |rhs|. +// +// This comparator is compatible with std::less. +// +// Address spaces go from most public to least public in the following order: +// +// - public and unknown +// - private +// - local +// +bool COMPONENT_EXPORT(NETWORK_CPP) + IsLessPublicAddressSpace(mojom::IPAddressSpace lhs, + mojom::IPAddressSpace rhs); + +} // namespace network + +#endif // SERVICES_NETWORK_PUBLIC_CPP_IP_ADDRESS_SPACE_UTIL_H_ diff --git a/chromium/services/network/public/cpp/ip_address_space_util_unittest.cc b/chromium/services/network/public/cpp/ip_address_space_util_unittest.cc new file mode 100644 index 00000000000..c29491b3a14 --- /dev/null +++ b/chromium/services/network/public/cpp/ip_address_space_util_unittest.cc @@ -0,0 +1,97 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/network/public/cpp/ip_address_space_util.h" + +#include "net/base/ip_address.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace network { +namespace { + +using mojom::IPAddressSpace; +using net::IPAddress; +using net::IPAddressBytes; + +TEST(IPAddressSpaceTest, IPAddressToIPAddressSpacev4) { + EXPECT_EQ(IPAddressToIPAddressSpace(IPAddress()), IPAddressSpace::kUnknown); + + EXPECT_EQ(IPAddressToIPAddressSpace(IPAddress(64, 233, 160, 0)), + IPAddressSpace::kPublic); + + EXPECT_EQ(IPAddressToIPAddressSpace(IPAddress(192, 168, 1, 1)), + IPAddressSpace::kPrivate); + EXPECT_EQ(IPAddressToIPAddressSpace(IPAddress(10, 1, 1, 1)), + IPAddressSpace::kPrivate); + + EXPECT_EQ(IPAddressToIPAddressSpace(IPAddress(127, 0, 0, 1)), + IPAddressSpace::kLocal); +} + +IPAddressBytes IPv6BytesWithPrefix(uint8_t prefix) { + IPAddressBytes bytes; + bytes.Resize(IPAddress::kIPv6AddressSize); + bytes.data()[0] = prefix; + return bytes; +} + +TEST(IPAddressSpaceTest, IPAddressToAddressSpacev6) { + EXPECT_EQ(IPAddressToIPAddressSpace(IPAddress(IPv6BytesWithPrefix(42))), + IPAddressSpace::kPublic); + + EXPECT_EQ(IPAddressToIPAddressSpace(IPAddress(IPv6BytesWithPrefix(0xfd))), + IPAddressSpace::kPrivate); + + EXPECT_EQ(IPAddressToIPAddressSpace(IPAddress::IPv6Localhost()), + IPAddressSpace::kLocal); +} + +TEST(IPAddressSpaceTest, IsLessPublicAddressSpaceThanLocal) { + EXPECT_FALSE( + IsLessPublicAddressSpace(IPAddressSpace::kLocal, IPAddressSpace::kLocal)); + + EXPECT_TRUE(IsLessPublicAddressSpace(IPAddressSpace::kLocal, + IPAddressSpace::kPrivate)); + EXPECT_TRUE(IsLessPublicAddressSpace(IPAddressSpace::kLocal, + IPAddressSpace::kPublic)); + EXPECT_TRUE(IsLessPublicAddressSpace(IPAddressSpace::kLocal, + IPAddressSpace::kUnknown)); +} + +TEST(IPAddressSpaceTest, IsLessPublicAddressSpaceThanPrivate) { + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kPrivate, + IPAddressSpace::kLocal)); + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kPrivate, + IPAddressSpace::kPrivate)); + + EXPECT_TRUE(IsLessPublicAddressSpace(IPAddressSpace::kPrivate, + IPAddressSpace::kPublic)); + EXPECT_TRUE(IsLessPublicAddressSpace(IPAddressSpace::kPrivate, + IPAddressSpace::kUnknown)); +} + +TEST(IPAddressSpaceTest, IsLessPublicAddressSpaceThanPublic) { + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kPublic, + IPAddressSpace::kLocal)); + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kPublic, + IPAddressSpace::kPrivate)); + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kPublic, + IPAddressSpace::kPublic)); + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kPublic, + IPAddressSpace::kUnknown)); +} + +TEST(IPAddressSpaceTest, IsLessPublicAddressSpaceThanUnknown) { + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kUnknown, + IPAddressSpace::kLocal)); + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kUnknown, + IPAddressSpace::kPrivate)); + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kUnknown, + IPAddressSpace::kPublic)); + EXPECT_FALSE(IsLessPublicAddressSpace(IPAddressSpace::kUnknown, + IPAddressSpace::kUnknown)); +} + +} // namespace +} // namespace network diff --git a/chromium/services/network/public/cpp/load_info_util.cc b/chromium/services/network/public/cpp/load_info_util.cc index 2a81a60198c..28394496e13 100644 --- a/chromium/services/network/public/cpp/load_info_util.cc +++ b/chromium/services/network/public/cpp/load_info_util.cc @@ -4,6 +4,7 @@ #include "services/network/public/cpp/load_info_util.h" +#include "net/base/load_states.h" #include "services/network/public/mojom/network_service.mojom.h" namespace network { diff --git a/chromium/services/network/public/cpp/net_ipc_param_traits.h b/chromium/services/network/public/cpp/net_ipc_param_traits.h index 0ad0fe96373..c3835ff3efa 100644 --- a/chromium/services/network/public/cpp/net_ipc_param_traits.h +++ b/chromium/services/network/public/cpp/net_ipc_param_traits.h @@ -8,6 +8,7 @@ #include <string> #include "base/component_export.h" +#include "base/memory/ref_counted.h" #include "base/pickle.h" #include "ipc/ipc_param_traits.h" #include "ipc/param_traits_macros.h" @@ -15,6 +16,7 @@ #include "net/base/host_port_pair.h" #include "net/base/ip_address.h" #include "net/base/ip_endpoint.h" +#include "net/base/load_timing_info.h" #include "net/base/proxy_server.h" #include "net/base/request_priority.h" #include "net/cert/cert_verify_result.h" @@ -23,12 +25,17 @@ #include "net/cert/signed_certificate_timestamp.h" #include "net/cert/signed_certificate_timestamp_and_status.h" #include "net/cert/x509_certificate.h" +#include "net/dns/public/resolve_error_info.h" #include "net/http/http_request_headers.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_response_info.h" #include "net/http/http_version.h" #include "net/nqe/effective_connection_type.h" #include "net/ssl/ssl_cert_request_info.h" #include "net/ssl/ssl_info.h" +#include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/redirect_info.h" +#include "net/url_request/referrer_policy.h" #include "url/ipc/url_param_traits.h" #include "url/origin.h" @@ -272,8 +279,7 @@ IPC_ENUM_TRAITS_MAX_VALUE(net::SSLClientCertType, IPC_ENUM_TRAITS_MAX_VALUE(net::SSLInfo::HandshakeType, net::SSLInfo::HANDSHAKE_FULL) -IPC_ENUM_TRAITS_MAX_VALUE(net::URLRequest::ReferrerPolicy, - net::URLRequest::MAX_REFERRER_POLICY) +IPC_ENUM_TRAITS_MAX_VALUE(net::ReferrerPolicy, net::ReferrerPolicy::MAX) IPC_STRUCT_TRAITS_BEGIN(net::HttpRequestHeaders::HeaderKeyValuePair) IPC_STRUCT_TRAITS_MEMBER(key) diff --git a/chromium/services/network/public/cpp/network_ipc_param_traits.cc b/chromium/services/network/public/cpp/network_ipc_param_traits.cc index ad07a680ea5..e0f982d4d34 100644 --- a/chromium/services/network/public/cpp/network_ipc_param_traits.cc +++ b/chromium/services/network/public/cpp/network_ipc_param_traits.cc @@ -57,6 +57,7 @@ void ParamTraits<network::DataElement>::Write(base::Pickle* m, .release()); break; } + case network::mojom::DataElementType::kReadOnceStream: case network::mojom::DataElementType::kUnknown: { NOTREACHED(); break; @@ -148,6 +149,7 @@ bool ParamTraits<network::DataElement>::Read(const base::Pickle* m, r->SetToChunkedDataPipe(std::move(chunked_data_pipe_getter)); return true; } + case network::mojom::DataElementType::kReadOnceStream: case network::mojom::DataElementType::kUnknown: { NOTREACHED(); return false; diff --git a/chromium/services/network/public/cpp/network_ipc_param_traits.h b/chromium/services/network/public/cpp/network_ipc_param_traits.h index 90fb4bf7d95..b8ee874ba32 100644 --- a/chromium/services/network/public/cpp/network_ipc_param_traits.h +++ b/chromium/services/network/public/cpp/network_ipc_param_traits.h @@ -25,7 +25,6 @@ #include "net/nqe/effective_connection_type.h" #include "net/ssl/ssl_cert_request_info.h" #include "net/ssl/ssl_info.h" -#include "net/url_request/redirect_info.h" #include "services/network/public/cpp/isolation_opt_in_hints.h" #include "services/network/public/cpp/net_ipc_param_traits.h" #include "services/network/public/cpp/origin_policy.h" diff --git a/chromium/services/network/public/cpp/not_implemented_url_loader_factory.cc b/chromium/services/network/public/cpp/not_implemented_url_loader_factory.cc new file mode 100644 index 00000000000..149e6734a6e --- /dev/null +++ b/chromium/services/network/public/cpp/not_implemented_url_loader_factory.cc @@ -0,0 +1,37 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/network/public/cpp/not_implemented_url_loader_factory.h" + +#include "base/logging.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "services/network/public/mojom/url_loader.mojom.h" + +namespace network { + +NotImplementedURLLoaderFactory::NotImplementedURLLoaderFactory() = default; + +NotImplementedURLLoaderFactory::~NotImplementedURLLoaderFactory() = default; + +void NotImplementedURLLoaderFactory::CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& url_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { + NOTREACHED(); + network::URLLoaderCompletionStatus status; + status.error_code = net::ERR_NOT_IMPLEMENTED; + mojo::Remote<network::mojom::URLLoaderClient>(std::move(client)) + ->OnComplete(status); +} + +void NotImplementedURLLoaderFactory::Clone( + mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) { + receivers_.Add(this, std::move(receiver)); +} + +} // namespace network diff --git a/chromium/services/network/public/cpp/not_implemented_url_loader_factory.h b/chromium/services/network/public/cpp/not_implemented_url_loader_factory.h new file mode 100644 index 00000000000..baca98d17a3 --- /dev/null +++ b/chromium/services/network/public/cpp/not_implemented_url_loader_factory.h @@ -0,0 +1,46 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_NETWORK_PUBLIC_CPP_NOT_IMPLEMENTED_URL_LOADER_FACTORY_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_NOT_IMPLEMENTED_URL_LOADER_FACTORY_H_ + +#include "base/component_export.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" + +namespace network { + +// A URLLoaderFactory which just fails to create a loader with +// net::ERR_NOT_IMPLEMENTED. +class COMPONENT_EXPORT(NETWORK_CPP) NotImplementedURLLoaderFactory final + : public network::mojom::URLLoaderFactory { + public: + NotImplementedURLLoaderFactory(); + ~NotImplementedURLLoaderFactory() override; + + // network::mojom::URLLoaderFactory implementation. + void CreateLoaderAndStart( + mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& url_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override; + + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + override; + + private: + mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_; + + DISALLOW_COPY_AND_ASSIGN(NotImplementedURLLoaderFactory); +}; + +} // namespace network + +#endif // SERVICES_NETWORK_PUBLIC_CPP_NOT_IMPLEMENTED_URL_LOADER_FACTORY_H_ diff --git a/chromium/services/network/public/cpp/optional_trust_token_params_unittest.cc b/chromium/services/network/public/cpp/optional_trust_token_params_unittest.cc index 7722e574bb2..35ea9b24717 100644 --- a/chromium/services/network/public/cpp/optional_trust_token_params_unittest.cc +++ b/chromium/services/network/public/cpp/optional_trust_token_params_unittest.cc @@ -27,7 +27,7 @@ OptionalTrustTokenParams NonemptyTrustTokenParams() { mojom::TrustTokenRefreshPolicy::kRefresh, mojom::TrustTokenSignRequestData::kInclude, /*include_timestamp_header=*/true, - url::Origin::Create(GURL("https://issuer.com")), + std::vector<url::Origin>{url::Origin::Create(GURL("https://issuer.com"))}, std::vector<std::string>{"some_header", "another_header"}, "some additional signing data"); } diff --git a/chromium/services/network/public/cpp/parsed_headers.cc b/chromium/services/network/public/cpp/parsed_headers.cc index dc7b9ae7adc..2ce3770b3a6 100644 --- a/chromium/services/network/public/cpp/parsed_headers.cc +++ b/chromium/services/network/public/cpp/parsed_headers.cc @@ -24,10 +24,12 @@ mojom::ParsedHeadersPtr PopulateParsedHeaders( AddContentSecurityPolicyFromHeaders(*headers, url, &parsed_headers->content_security_policy); + parsed_headers->allow_csp_from = ParseAllowCSPFromHeader(*headers); + parsed_headers->cross_origin_embedder_policy = ParseCrossOriginEmbedderPolicy(*headers); - parsed_headers->cross_origin_opener_policy = - ParseCrossOriginOpenerPolicy(*headers); + parsed_headers->cross_origin_opener_policy = ParseCrossOriginOpenerPolicy( + *headers, parsed_headers->cross_origin_embedder_policy); std::string origin_isolation; if (headers->GetNormalizedHeader("Origin-Isolation", &origin_isolation)) @@ -37,6 +39,12 @@ mojom::ParsedHeadersPtr PopulateParsedHeaders( if (headers->GetNormalizedHeader("Accept-CH", &accept_ch)) parsed_headers->accept_ch = ParseAcceptCH(accept_ch); + std::string accept_ch_lifetime; + if (headers->GetNormalizedHeader("Accept-CH-Lifetime", &accept_ch_lifetime)) { + parsed_headers->accept_ch_lifetime = + ParseAcceptCHLifetime(accept_ch_lifetime); + } + return parsed_headers; } diff --git a/chromium/services/network/public/cpp/proxy_config_mojom_traits_unittest.cc b/chromium/services/network/public/cpp/proxy_config_mojom_traits_unittest.cc index f64ff8e4ca8..b5ec2918e6a 100644 --- a/chromium/services/network/public/cpp/proxy_config_mojom_traits_unittest.cc +++ b/chromium/services/network/public/cpp/proxy_config_mojom_traits_unittest.cc @@ -62,8 +62,8 @@ TEST(ProxyConfigTraitsTest, ProxyRules) { "ftp=ftp1,ftp2,ftp3", "http=http1,http2; http=http3", "ftp=ftp1,ftp2,ftp3 ; http=http1,http2; ", - "http=https://secure_proxy; ftp=socks4://socks_proxy; " - "https=socks://foo", + ("http=https://secure_proxy; ftp=socks4://socks_proxy; " + "https=socks://foo"), "socks=foopy", "http=httpproxy ; https=httpsproxy ; ftp=ftpproxy ; socks=foopy ", "http=httpproxy ; https=httpsproxy ; socks=socks5://foopy ", diff --git a/chromium/services/network/public/cpp/resource_request.cc b/chromium/services/network/public/cpp/resource_request.cc index 7d32256054a..d258166332b 100644 --- a/chromium/services/network/public/cpp/resource_request.cc +++ b/chromium/services/network/public/cpp/resource_request.cc @@ -128,34 +128,32 @@ bool ResourceRequest::SavesCookies() const { !(load_flags & net::LOAD_DO_NOT_SAVE_COOKIES); } -net::URLRequest::ReferrerPolicy ReferrerPolicyForUrlRequest( +net::ReferrerPolicy ReferrerPolicyForUrlRequest( mojom::ReferrerPolicy referrer_policy) { switch (referrer_policy) { case mojom::ReferrerPolicy::kAlways: - return net::URLRequest::NEVER_CLEAR_REFERRER; + return net::ReferrerPolicy::NEVER_CLEAR; case mojom::ReferrerPolicy::kNever: - return net::URLRequest::NO_REFERRER; + return net::ReferrerPolicy::NO_REFERRER; case mojom::ReferrerPolicy::kOrigin: - return net::URLRequest::ORIGIN; + return net::ReferrerPolicy::ORIGIN; case mojom::ReferrerPolicy::kNoReferrerWhenDowngrade: - return net::URLRequest:: - CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE; + return net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE; case mojom::ReferrerPolicy::kOriginWhenCrossOrigin: - return net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; + return net::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; case mojom::ReferrerPolicy::kSameOrigin: - return net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN; + return net::ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN; case mojom::ReferrerPolicy::kStrictOrigin: - return net::URLRequest:: + return net::ReferrerPolicy:: ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE; case mojom::ReferrerPolicy::kDefault: CHECK(false); - return net::URLRequest::NO_REFERRER; + return net::ReferrerPolicy::NO_REFERRER; case mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin: - return net::URLRequest:: - REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; + return net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; } NOTREACHED(); - return net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE; + return net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE; } } // namespace network diff --git a/chromium/services/network/public/cpp/resource_request.h b/chromium/services/network/public/cpp/resource_request.h index 44cb6c0964e..d911066399e 100644 --- a/chromium/services/network/public/cpp/resource_request.h +++ b/chromium/services/network/public/cpp/resource_request.h @@ -17,7 +17,7 @@ #include "net/base/request_priority.h" #include "net/cookies/site_for_cookies.h" #include "net/http/http_request_headers.h" -#include "net/url_request/url_request.h" +#include "net/url_request/referrer_policy.h" #include "services/network/public/cpp/optional_trust_token_params.h" #include "services/network/public/cpp/resource_request_body.h" #include "services/network/public/mojom/cookie_access_observer.mojom.h" @@ -73,8 +73,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { base::Optional<url::Origin> request_initiator; base::Optional<url::Origin> isolated_world_origin; GURL referrer; - net::URLRequest::ReferrerPolicy referrer_policy = - net::URLRequest::NEVER_CLEAR_REFERRER; + net::ReferrerPolicy referrer_policy = net::ReferrerPolicy::NEVER_CLEAR; net::HttpRequestHeaders headers; net::HttpRequestHeaders cors_exempt_headers; int load_flags = 0; @@ -123,7 +122,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest { // This does not accept |kDefault| referrer policy. COMPONENT_EXPORT(NETWORK_CPP_BASE) -net::URLRequest::ReferrerPolicy ReferrerPolicyForUrlRequest( +net::ReferrerPolicy ReferrerPolicyForUrlRequest( mojom::ReferrerPolicy referrer_policy); } // namespace network diff --git a/chromium/services/network/public/cpp/resource_request_body.cc b/chromium/services/network/public/cpp/resource_request_body.cc index a865aeb4395..8797143dde6 100644 --- a/chromium/services/network/public/cpp/resource_request_body.cc +++ b/chromium/services/network/public/cpp/resource_request_body.cc @@ -21,9 +21,15 @@ scoped_refptr<ResourceRequestBody> ResourceRequestBody::CreateFromBytes( return result; } +bool ResourceRequestBody::EnableToAppendElement() const { + return elements_.empty() || + (elements_.front().type() != + mojom::DataElementType::kChunkedDataPipe && + elements_.front().type() != mojom::DataElementType::kReadOnceStream); +} + void ResourceRequestBody::AppendBytes(std::vector<uint8_t> bytes) { - DCHECK(elements_.empty() || - elements_.front().type() != mojom::DataElementType::kChunkedDataPipe); + DCHECK(EnableToAppendElement()); if (bytes.size() > 0) { elements_.push_back(DataElement()); @@ -44,8 +50,7 @@ void ResourceRequestBody::AppendFileRange( uint64_t offset, uint64_t length, const base::Time& expected_modification_time) { - DCHECK(elements_.empty() || - elements_.front().type() != mojom::DataElementType::kChunkedDataPipe); + DCHECK(EnableToAppendElement()); elements_.push_back(DataElement()); elements_.back().SetToFilePathRange(file_path, offset, length, @@ -58,8 +63,7 @@ void ResourceRequestBody::AppendRawFileRange( uint64_t offset, uint64_t length, const base::Time& expected_modification_time) { - DCHECK(elements_.empty() || - elements_.front().type() != mojom::DataElementType::kChunkedDataPipe); + DCHECK(EnableToAppendElement()); elements_.push_back(DataElement()); elements_.back().SetToFileRange(std::move(file), file_path, offset, length, @@ -71,8 +75,7 @@ void ResourceRequestBody::AppendBlob(const std::string& uuid) { } void ResourceRequestBody::AppendBlob(const std::string& uuid, uint64_t length) { - DCHECK(elements_.empty() || - elements_.front().type() != mojom::DataElementType::kChunkedDataPipe); + DCHECK(EnableToAppendElement()); elements_.push_back(DataElement()); elements_.back().SetToBlobRange(uuid, 0 /* offset */, length); @@ -80,8 +83,7 @@ void ResourceRequestBody::AppendBlob(const std::string& uuid, uint64_t length) { void ResourceRequestBody::AppendDataPipe( mojo::PendingRemote<mojom::DataPipeGetter> data_pipe_getter) { - DCHECK(elements_.empty() || - elements_.front().type() != mojom::DataElementType::kChunkedDataPipe); + DCHECK(EnableToAppendElement()); elements_.push_back(DataElement()); elements_.back().SetToDataPipe(std::move(data_pipe_getter)); @@ -96,6 +98,15 @@ void ResourceRequestBody::SetToChunkedDataPipe( elements_.back().SetToChunkedDataPipe(std::move(chunked_data_pipe_getter)); } +void ResourceRequestBody::SetToReadOnceStream( + mojo::PendingRemote<mojom::ChunkedDataPipeGetter> + chunked_data_pipe_getter) { + DCHECK(elements_.empty()); + + elements_.push_back(DataElement()); + elements_.back().SetToReadOnceStream(std::move(chunked_data_pipe_getter)); +} + std::vector<base::FilePath> ResourceRequestBody::GetReferencedFiles() const { std::vector<base::FilePath> result; for (const auto& element : *elements()) { diff --git a/chromium/services/network/public/cpp/resource_request_body.h b/chromium/services/network/public/cpp/resource_request_body.h index 2634327fdac..e3679f2b1e7 100644 --- a/chromium/services/network/public/cpp/resource_request_body.h +++ b/chromium/services/network/public/cpp/resource_request_body.h @@ -80,6 +80,10 @@ class COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequestBody // support chunked uploads. void SetToChunkedDataPipe(mojo::PendingRemote<mojom::ChunkedDataPipeGetter> chunked_data_pipe_getter); + // Almost same as above except |chunked_data_pipe_getter| is read only once + // and you must talk with a server supporting chunked upload. + void SetToReadOnceStream(mojo::PendingRemote<mojom::ChunkedDataPipeGetter> + chunked_data_pipe_getter); void SetAllowHTTP1ForStreamingUpload(bool allow) { allow_http1_for_streaming_upload_ = allow; } @@ -118,6 +122,8 @@ class COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequestBody scoped_refptr<network::ResourceRequestBody>>; ~ResourceRequestBody(); + bool EnableToAppendElement() const; + std::vector<DataElement> elements_; int64_t identifier_; diff --git a/chromium/services/network/public/cpp/session_cookie_delete_predicate.h b/chromium/services/network/public/cpp/session_cookie_delete_predicate.h new file mode 100644 index 00000000000..8b2162d8705 --- /dev/null +++ b/chromium/services/network/public/cpp/session_cookie_delete_predicate.h @@ -0,0 +1,23 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_NETWORK_PUBLIC_CPP_SESSION_COOKIE_DELETE_PREDICATE_H_ +#define SERVICES_NETWORK_PUBLIC_CPP_SESSION_COOKIE_DELETE_PREDICATE_H_ + +#include <string> + +#include "base/callback_forward.h" + +namespace network { +// A DeleteCookiePredicate callback function decides if the cookie associated +// with the domain and is_https status should be deleted on exit, and is used +// when creating a cookie storage policy. It has two parameters, the first one +// is the domain of a cookie and the second one is a bool which represents +// whether the cookie is secure as parameters. +using DeleteCookiePredicate = + base::RepeatingCallback<bool(const std::string&, bool)>; + +} // namespace network + +#endif // SERVICES_NETWORK_PUBLIC_CPP_SESSION_COOKIE_DELETE_PREDICATE_H_ diff --git a/chromium/services/network/public/cpp/simple_url_loader.cc b/chromium/services/network/public/cpp/simple_url_loader.cc index e9e0c936a64..3e505a01541 100644 --- a/chromium/services/network/public/cpp/simple_url_loader.cc +++ b/chromium/services/network/public/cpp/simple_url_loader.cc @@ -999,7 +999,7 @@ class SaveToFileBodyHandler : public BodyHandler { on_done_callback_.Reset(); DCHECK(!path_.empty()); - base::DeleteFile(path_, false /* recursive */); + base::DeleteFile(path_); owns_file_ = false; } diff --git a/chromium/services/network/public/cpp/simple_url_loader_unittest.cc b/chromium/services/network/public/cpp/simple_url_loader_unittest.cc index beea0a11e91..afce68ff2d9 100644 --- a/chromium/services/network/public/cpp/simple_url_loader_unittest.cc +++ b/chromium/services/network/public/cpp/simple_url_loader_unittest.cc @@ -47,6 +47,7 @@ #include "net/test/embedded_test_server/http_response.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/redirect_info.h" +#include "net/url_request/url_request.h" #include "services/network/network_service.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/simple_url_loader_stream_consumer.h" @@ -375,7 +376,7 @@ class SimpleLoaderTestHelper : public SimpleURLLoaderStreamConsumer { // Clean up file, so tests don't leave around files in the temp directory. // Only matters in the TO_TEMP_FILE case. if (!file_path.empty()) - base::DeleteFile(file_path, false); + base::DeleteFile(file_path); done_ = true; run_loop_.Quit(); diff --git a/chromium/services/network/public/cpp/url_request_mojom_traits.cc b/chromium/services/network/public/cpp/url_request_mojom_traits.cc index ce1478f6df6..34f6223f495 100644 --- a/chromium/services/network/public/cpp/url_request_mojom_traits.cc +++ b/chromium/services/network/public/cpp/url_request_mojom_traits.cc @@ -74,80 +74,70 @@ bool EnumTraits<network::mojom::RequestPriority, net::RequestPriority>:: return true; } -network::mojom::URLRequestReferrerPolicy EnumTraits< - network::mojom::URLRequestReferrerPolicy, - net::URLRequest::ReferrerPolicy>::ToMojom(net::URLRequest::ReferrerPolicy - policy) { +network::mojom::URLRequestReferrerPolicy +EnumTraits<network::mojom::URLRequestReferrerPolicy, + net::ReferrerPolicy>::ToMojom(net::ReferrerPolicy policy) { switch (policy) { - case net::URLRequest::ReferrerPolicy:: - CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE: + case net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE: return network::mojom::URLRequestReferrerPolicy:: kClearReferrerOnTransitionFromSecureToInsecure; - case net::URLRequest::ReferrerPolicy:: - REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN: + case net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN: return network::mojom::URLRequestReferrerPolicy:: kReduceReferrerGranularityOnTransitionCrossOrigin; - case net::URLRequest::ReferrerPolicy:: - ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN: + case net::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN: return network::mojom::URLRequestReferrerPolicy:: kOriginOnlyOnTransitionCrossOrigin; - case net::URLRequest::ReferrerPolicy::NEVER_CLEAR_REFERRER: + case net::ReferrerPolicy::NEVER_CLEAR: return network::mojom::URLRequestReferrerPolicy::kNeverClearReferrer; - case net::URLRequest::ReferrerPolicy::ORIGIN: + case net::ReferrerPolicy::ORIGIN: return network::mojom::URLRequestReferrerPolicy::kOrigin; - case net::URLRequest::ReferrerPolicy:: - CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN: + case net::ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN: return network::mojom::URLRequestReferrerPolicy:: kClearReferrerOnTransitionCrossOrigin; - case net::URLRequest::ReferrerPolicy:: + case net::ReferrerPolicy:: ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE: return network::mojom::URLRequestReferrerPolicy:: kOriginClearOnTransitionFromSecureToInsecure; - case net::URLRequest::ReferrerPolicy::NO_REFERRER: + case net::ReferrerPolicy::NO_REFERRER: return network::mojom::URLRequestReferrerPolicy::kNoReferrer; } NOTREACHED(); return static_cast<network::mojom::URLRequestReferrerPolicy>(policy); } -bool EnumTraits<network::mojom::URLRequestReferrerPolicy, - net::URLRequest::ReferrerPolicy>:: +bool EnumTraits<network::mojom::URLRequestReferrerPolicy, net::ReferrerPolicy>:: FromMojom(network::mojom::URLRequestReferrerPolicy in, - net::URLRequest::ReferrerPolicy* out) { + net::ReferrerPolicy* out) { switch (in) { case network::mojom::URLRequestReferrerPolicy:: kClearReferrerOnTransitionFromSecureToInsecure: - *out = net::URLRequest::ReferrerPolicy:: - CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE; + *out = net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE; return true; case network::mojom::URLRequestReferrerPolicy:: kReduceReferrerGranularityOnTransitionCrossOrigin: - *out = net::URLRequest::ReferrerPolicy:: - REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; + *out = net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN; return true; case network::mojom::URLRequestReferrerPolicy:: kOriginOnlyOnTransitionCrossOrigin: - *out = net::URLRequest::ReferrerPolicy:: - ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; + *out = net::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; return true; case network::mojom::URLRequestReferrerPolicy::kNeverClearReferrer: - *out = net::URLRequest::ReferrerPolicy::NEVER_CLEAR_REFERRER; + *out = net::ReferrerPolicy::NEVER_CLEAR; return true; case network::mojom::URLRequestReferrerPolicy::kOrigin: - *out = net::URLRequest::ReferrerPolicy::ORIGIN; + *out = net::ReferrerPolicy::ORIGIN; return true; case network::mojom::URLRequestReferrerPolicy:: kClearReferrerOnTransitionCrossOrigin: - *out = net::URLRequest::ReferrerPolicy:: - CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN; + *out = net::ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN; return true; case network::mojom::URLRequestReferrerPolicy:: kOriginClearOnTransitionFromSecureToInsecure: - *out = net::URLRequest::ReferrerPolicy:: + *out = net::ReferrerPolicy:: ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE; return true; case network::mojom::URLRequestReferrerPolicy::kNoReferrer: - *out = net::URLRequest::ReferrerPolicy::NO_REFERRER; + *out = net::ReferrerPolicy::NO_REFERRER; return true; } diff --git a/chromium/services/network/public/cpp/url_request_mojom_traits.h b/chromium/services/network/public/cpp/url_request_mojom_traits.h index 9e1dd066185..f3daedb4570 100644 --- a/chromium/services/network/public/cpp/url_request_mojom_traits.h +++ b/chromium/services/network/public/cpp/url_request_mojom_traits.h @@ -17,7 +17,7 @@ #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/struct_traits.h" #include "net/base/request_priority.h" -#include "net/url_request/url_request_job.h" +#include "net/url_request/referrer_policy.h" #include "services/network/public/cpp/data_element.h" #include "services/network/public/cpp/network_isolation_key_mojom_traits.h" #include "services/network/public/cpp/resource_request.h" @@ -42,12 +42,11 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) template <> struct COMPONENT_EXPORT(NETWORK_CPP_BASE) - EnumTraits<network::mojom::URLRequestReferrerPolicy, - net::URLRequest::ReferrerPolicy> { + EnumTraits<network::mojom::URLRequestReferrerPolicy, net::ReferrerPolicy> { static network::mojom::URLRequestReferrerPolicy ToMojom( - net::URLRequest::ReferrerPolicy policy); + net::ReferrerPolicy policy); static bool FromMojom(network::mojom::URLRequestReferrerPolicy in, - net::URLRequest::ReferrerPolicy* out); + net::ReferrerPolicy* out); }; template <> @@ -112,7 +111,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) static const GURL& referrer(const network::ResourceRequest& request) { return request.referrer; } - static net::URLRequest::ReferrerPolicy referrer_policy( + static net::ReferrerPolicy referrer_policy( const network::ResourceRequest& request) { return request.referrer_policy; } @@ -328,7 +327,8 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) } static mojo::PendingRemote<network::mojom::ChunkedDataPipeGetter> chunked_data_pipe_getter(const network::DataElement& element) { - if (element.type_ != network::mojom::DataElementType::kChunkedDataPipe) + if (element.type_ != network::mojom::DataElementType::kChunkedDataPipe && + element.type_ != network::mojom::DataElementType::kReadOnceStream) return mojo::NullRemote(); return const_cast<network::DataElement&>(element) .ReleaseChunkedDataPipeGetter(); diff --git a/chromium/services/network/public/cpp/url_request_mojom_traits_unittest.cc b/chromium/services/network/public/cpp/url_request_mojom_traits_unittest.cc index 2f104d7306a..7daeae3834a 100644 --- a/chromium/services/network/public/cpp/url_request_mojom_traits_unittest.cc +++ b/chromium/services/network/public/cpp/url_request_mojom_traits_unittest.cc @@ -8,6 +8,7 @@ #include "mojo/public/cpp/base/unguessable_token_mojom_traits.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "net/base/isolation_info.h" +#include "net/url_request/referrer_policy.h" #include "services/network/public/cpp/http_request_headers_mojom_traits.h" #include "services/network/public/cpp/network_ipc_param_traits.h" #include "services/network/public/cpp/optional_trust_token_params.h" @@ -21,27 +22,22 @@ namespace { TEST(URLRequestMojomTraitsTest, Roundtrips_URLRequestReferrerPolicy) { for (auto referrer_policy : - {net::URLRequest::ReferrerPolicy:: - CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE, - net::URLRequest::ReferrerPolicy:: - REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, - net::URLRequest::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, - net::URLRequest::ReferrerPolicy::NEVER_CLEAR_REFERRER, - net::URLRequest::ReferrerPolicy::ORIGIN, - net::URLRequest::ReferrerPolicy:: - CLEAR_REFERRER_ON_TRANSITION_CROSS_ORIGIN, - net::URLRequest::ReferrerPolicy:: - ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, - net::URLRequest::ReferrerPolicy::NO_REFERRER}) { + {net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, + net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN, + net::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN, + net::ReferrerPolicy::NEVER_CLEAR, net::ReferrerPolicy::ORIGIN, + net::ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN, + net::ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE, + net::ReferrerPolicy::NO_REFERRER}) { int32_t serialized = -1; using URLRequestReferrerPolicySerializer = mojo::internal::Serializer<mojom::URLRequestReferrerPolicy, - net::URLRequest::ReferrerPolicy>; + net::ReferrerPolicy>; URLRequestReferrerPolicySerializer::Serialize(referrer_policy, &serialized); - EXPECT_EQ(referrer_policy, serialized); - net::URLRequest::ReferrerPolicy deserialized; + EXPECT_EQ(static_cast<int32_t>(referrer_policy), serialized); + net::ReferrerPolicy deserialized; URLRequestReferrerPolicySerializer::Deserialize(serialized, &deserialized); - EXPECT_EQ(serialized, deserialized); + EXPECT_EQ(referrer_policy, deserialized); } } @@ -58,7 +54,7 @@ TEST(URLRequestMojomTraitsTest, Roundtrips_ResourceRequest) { url::Origin::Create(GURL("chrome-extension://blah")); original.referrer = GURL("https://referrer.com/"); original.referrer_policy = - net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; + net::ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN; original.headers.SetHeader("Accept", "text/xml"); original.cors_exempt_headers.SetHeader("X-Requested-With", "ForTesting"); original.load_flags = 3; @@ -97,8 +93,8 @@ TEST(URLRequestMojomTraitsTest, Roundtrips_ResourceRequest) { original.trusted_params->disable_secure_dns = true; original.trust_token_params = network::mojom::TrustTokenParams(); - original.trust_token_params->issuer = - url::Origin::Create(GURL("https://issuer.com")); + original.trust_token_params->issuers.push_back( + url::Origin::Create(GURL("https://issuer.com"))); original.trust_token_params->type = mojom::TrustTokenOperationType::kRedemption; original.trust_token_params->include_timestamp_header = true; diff --git a/chromium/services/network/public/mojom/BUILD.gn b/chromium/services/network/public/mojom/BUILD.gn index 4d5bef25d3c..c659ffa75bc 100644 --- a/chromium/services/network/public/mojom/BUILD.gn +++ b/chromium/services/network/public/mojom/BUILD.gn @@ -385,16 +385,17 @@ mojom("cookies_mojom") { move_only = true }, { - mojom = "network.mojom.CookieWithStatus" - cpp = "::net::CookieWithStatus" + mojom = "network.mojom.CookieAccessResult" + cpp = "::net::CookieAccessResult" + move_only = true }, { mojom = "network.mojom.CookieWithAccessResult" cpp = "::net::CookieWithAccessResult" }, { - mojom = "network.mojom.CookieAndLineWithStatus" - cpp = "::net::CookieAndLineWithStatus" + mojom = "network.mojom.CookieAndLineWithAccessResult" + cpp = "::net::CookieAndLineWithAccessResult" }, { mojom = "network.mojom.CookieChangeCause" @@ -416,9 +417,20 @@ mojom("cookies_mojom") { traits_public_deps = [ "//net" ] }, ] - cpp_typemaps += shared_cpp_typemaps - blink_cpp_typemaps = shared_cpp_typemaps + + blink_cpp_typemaps = [ + { + types = [ + { + mojom = "network.mojom.CanonicalCookie" + cpp = "::blink::CanonicalCookie" + }, + ] + traits_headers = [ "//third_party/blink/renderer/platform/cookie/canonical_cookie_mojom_traits.h" ] + }, + ] + blink_cpp_typemaps += shared_cpp_typemaps } mojom("mojom") { @@ -561,7 +573,7 @@ mojom("mojom") { }, { mojom = "network.mojom.URLRequestReferrerPolicy" - cpp = "::net::URLRequest::ReferrerPolicy" + cpp = "::net::ReferrerPolicy" }, { mojom = "network.mojom.RequestPriority" diff --git a/chromium/services/network/public/mojom/content_security_policy.mojom b/chromium/services/network/public/mojom/content_security_policy.mojom index ca7d5062a8f..8c763092363 100644 --- a/chromium/services/network/public/mojom/content_security_policy.mojom +++ b/chromium/services/network/public/mojom/content_security_policy.mojom @@ -4,6 +4,7 @@ module network.mojom; +import "url/mojom/origin.mojom"; import "url/mojom/url.mojom"; import "services/network/public/mojom/source_location.mojom"; import "services/network/public/mojom/web_sandbox_flags.mojom"; @@ -62,32 +63,67 @@ struct CSPSource { bool is_port_wildcard = false; }; +enum CSPHashAlgorithm { + SHA256, + SHA384, + SHA512, +}; + +struct CSPHashSource { + CSPHashAlgorithm algorithm; + string value; +}; + struct CSPSourceList { array<CSPSource> sources; + array<string> nonces; + array<CSPHashSource> hashes; // Wildcard hosts and 'self' aren't stored in |sources|, but as attributes // on the source list itself. bool allow_self = false; bool allow_star = false; bool allow_response_redirects = false; + bool allow_inline = false; + bool allow_eval = false; + bool allow_wasm_eval = false; + bool allow_dynamic = false; + bool allow_unsafe_hashes = false; + bool report_sample = false; }; enum CSPDirectiveName { Unknown, - DefaultSrc, + BaseURI, + BlockAllMixedContent, ChildSrc, - FrameSrc, + ConnectSrc, + DefaultSrc, + FontSrc, FormAction, - NavigateTo, FrameAncestors, + FrameSrc, ImgSrc, + ManifestSrc, MediaSrc, + NavigateTo, ObjectSrc, + PrefetchSrc, + ReportTo, + ReportURI, + RequireTrustedTypesFor, + Sandbox, ScriptSrc, + ScriptSrcAttr, + ScriptSrcElem, StyleSrc, + StyleSrcAttr, + StyleSrcElem, + TreatAsPublicAddress, + TrustedTypes, + UpgradeInsecureRequests, WorkerSrc, - ConnectSrc }; struct ContentSecurityPolicy { @@ -116,6 +152,9 @@ struct ContentSecurityPolicy { // Set of reporting endpoints to which violation reports are sent. array<string> report_endpoints; + + // An array containing a set of errors occurred while parsing the CSP header. + array<string> parsing_errors; }; // Data to report Content-Security-Policy violations. @@ -155,3 +194,14 @@ struct CSPViolation { // The source code location that triggered the blocked navigation. SourceLocation source_location; }; + +union AllowCSPFromHeaderValue { + // Set to true if the 'Allow-CSP-From' header just contains '*'. + bool allow_star; + + // The parsed origin contained in the 'Allow-CSP-From' header. + url.mojom.Origin origin; + + // If the header value is invalid, an error message will be stored here. + string error_message; +};
\ No newline at end of file diff --git a/chromium/services/network/public/mojom/cookie_access_observer.mojom b/chromium/services/network/public/mojom/cookie_access_observer.mojom index 8cf5dd7ed5c..04dc1ae40b2 100644 --- a/chromium/services/network/public/mojom/cookie_access_observer.mojom +++ b/chromium/services/network/public/mojom/cookie_access_observer.mojom @@ -23,7 +23,7 @@ struct CookieAccessDetails { url.mojom.Url url; SiteForCookies site_for_cookies; - array<CookieWithStatus> cookie_list; + array<CookieWithAccessResult> cookie_list; // |devtools_request_id| contains the DevTools request id of the request // that triggered the cookie change, if the read was triggered by a request. diff --git a/chromium/services/network/public/mojom/cookie_manager.mojom b/chromium/services/network/public/mojom/cookie_manager.mojom index 07aaee6a595..efb5fa353ef 100644 --- a/chromium/services/network/public/mojom/cookie_manager.mojom +++ b/chromium/services/network/public/mojom/cookie_manager.mojom @@ -83,6 +83,7 @@ enum CookieEffectiveSameSite { kLaxMode = 1, kStrictMode = 2, kLaxModeAllowUnsafe = 3, + kUndefined = 4, }; enum ContextType { @@ -141,19 +142,15 @@ struct CookieInclusionStatus { uint32 warning_reasons; }; -struct CookieWithStatus { - CanonicalCookie cookie; - CookieInclusionStatus status; -}; - -struct CookieAndLineWithStatus { +struct CookieAndLineWithAccessResult { CanonicalCookie? cookie; string cookie_string; - CookieInclusionStatus status; + CookieAccessResult access_result; }; struct CookieAccessResult { CookieEffectiveSameSite effective_same_site; + CookieAccessSemantics access_semantics; CookieInclusionStatus status; }; @@ -184,8 +181,8 @@ enum CookieChangeCause { struct CookieChangeInfo { // The cookie that changed, in its post-change state. CanonicalCookie cookie; - // Access semantics of the cookie at the time of the change. - CookieAccessSemantics access_semantics; + // Access results at the time of the change. + CookieAccessResult access_result; CookieChangeCause cause; }; @@ -305,7 +302,7 @@ interface CookieManager { // |status| will be set to INCLUDE. SetCanonicalCookie(CanonicalCookie cookie, url.mojom.Url source_url, CookieOptions cookie_options) - => (CookieInclusionStatus status); + => (CookieAccessResult access_result); // Delete a cookie. Returns true if a cookie was deleted. DeleteCanonicalCookie(CanonicalCookie cookie) => (bool success); diff --git a/chromium/services/network/public/mojom/cross_origin_opener_policy.mojom b/chromium/services/network/public/mojom/cross_origin_opener_policy.mojom index ac96351bc7a..8afb0851d96 100644 --- a/chromium/services/network/public/mojom/cross_origin_opener_policy.mojom +++ b/chromium/services/network/public/mojom/cross_origin_opener_policy.mojom @@ -5,23 +5,28 @@ module network.mojom; import "url/mojom/url.mojom"; +import "mojo/public/mojom/base/unguessable_token.mojom"; +import "services/network/public/mojom/source_location.mojom"; + +enum CoopAccessReportType { + kAccessFromCoopPageToOpener, + kAccessFromCoopPageToOpenee, + kAccessFromCoopPageToOther, + kAccessToCoopPageFromOpener, + kAccessToCoopPageFromOpenee, + kAccessToCoopPageFromOther, +}; // Reports potential COOP violations. Implemented in the browser process. // TODO(ahemery, pmeuleman): Add extra coop breakage cases as listed in // https://docs.google.com/document/d/1zWqwI8PFrezwQpBSejIMUfdtsIYl9-h8epasdrDXVIM/edit interface CrossOriginOpenerPolicyReporter { - - // When COOP triggers a browsing context group swap during a navigation, we - // lose the existing opener, which can create page breakage. We report such - // cases using this function. - // |is_reported_from_document| is true if the report is coming from the - // document begin navigated from. It is false if the report originates from - // the document we are navigating to. - // |is_report_only| is true if we are reporting a breakage that would have - // occurred if we enforced the reporting only values of COOP. - QueueOpenerBreakageReport(url.mojom.Url other_url, - bool is_reported_from_document, - bool is_report_only); + // Sends a report when two browsing contexts from different virtual browsing + // context groups tries to access each other. + // - |property| is the name of the access property (postMessage, open, ...). + // - |source_location| represents the line of code that triggered the access. + QueueAccessReport(CoopAccessReportType report_type, string property, + SourceLocation source_location); // Connects a new pipe to this instance. Clone(pending_receiver<CrossOriginOpenerPolicyReporter> receiver); @@ -40,7 +45,10 @@ enum CrossOriginOpenerPolicyValue { // No restriction is applied, the relationship is kept with openers and // opened documents. - kUnsafeNone + kUnsafeNone, + + // Same origin with Cross-Origin-Embedder-Policy: require-corp. + kSameOriginPlusCoep, }; // Cross-Origin-Opener-Policy enum representing parsed values. @@ -58,4 +66,3 @@ struct CrossOriginOpenerPolicy { // The reporting endpoint for the "report only" mode. string? report_only_reporting_endpoint; }; - diff --git a/chromium/services/network/public/mojom/host_resolver.mojom b/chromium/services/network/public/mojom/host_resolver.mojom index aa9566a011e..7d48d2a4620 100644 --- a/chromium/services/network/public/mojom/host_resolver.mojom +++ b/chromium/services/network/public/mojom/host_resolver.mojom @@ -75,10 +75,6 @@ struct DnsConfigOverrides { // Whether suffix search should be performed for multi-label names. Tristate append_to_multi_label_name = Tristate.NO_OVERRIDE; - // Whether source port randomization is required. This uses additional - // resources on some platforms. - Tristate randomize_ports = Tristate.NO_OVERRIDE; - // Minimum number of dots before global resolution precedes |search|. int8 ndots = -1; // -1 for no override. diff --git a/chromium/services/network/public/mojom/network_context.mojom b/chromium/services/network/public/mojom/network_context.mojom index 6d5110add0e..87b26150440 100644 --- a/chromium/services/network/public/mojom/network_context.mojom +++ b/chromium/services/network/public/mojom/network_context.mojom @@ -441,9 +441,6 @@ struct NetworkContextParams { // policy. array<string> hsts_policy_bypass_list; - // Extra CORS safelisted request headers names. - array<string> cors_extra_safelisted_request_header_names; - // Specifies the CORS mode. // kDefault - Decided by base::Feature // kEnable - Enable CORS in Network Service (aka OOR-CORS) @@ -574,16 +571,35 @@ struct URLLoaderFactoryOverride { bool skip_cors_enabled_scheme_check = false; }; +// How to treat private network requests. +// +// Private network requests are any requests to a resource served by a +// non-public IP address. +// +// See the CORS-RFC1918 spec for details: https://wicg.github.io/cors-rfc1918. +enum PrivateNetworkRequestPolicy { + // Allow all requests. + kAllow, + + // Forbid requests to more-private address spaces than that of the initiator, + // when the initiator is not in a secure context. + kBlockFromInsecureToMorePrivate, +}; + struct ClientSecurityState { // https://mikewest.github.io/corpp/#integration-html // https://mikewest.github.io/corpp/#initialize-embedder-policy-for-global CrossOriginEmbedderPolicy cross_origin_embedder_policy; // Whether the initiator of the requests is in a web secure context. + // See: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts bool is_web_secure_context = false; // The initiator's IP AddressSpace. - IPAddressSpace ip_address_space = IPAddressSpace.kUnknown; + IPAddressSpace ip_address_space = kUnknown; + + // The policy to apply to private network requests. + PrivateNetworkRequestPolicy private_network_request_policy = kAllow; }; // Whether to forbid all Trust Tokens redemption and signing operations @@ -598,11 +614,14 @@ struct URLLoaderFactoryParams { // Set to kBrowserProcessId to indicate the browser process. int32 process_id = kInvalidProcessId; - // If specified, then |request_initiator_site_lock| locks + // If specified, then |request_initiator_origin_lock| locks // |ResourceRequest::request_initiator| to the specified origin. - // TODO(lukasza): https://crbug.com/891872: 1) Make this non-optional, - // 2) Make this an *origin* lock (rather than a *site* lock). - url.mojom.Origin? request_initiator_site_lock; + // + // SECURITY NOTE: |request_initiator_origin_lock| should be present in all + // factories that may be vended to a Renderer process. + // |request_initiator_origin_lock| may be missing only in factories used by + // the Browser process. + url.mojom.Origin? request_initiator_origin_lock; // Cross-origin read blocking (CORB) configuration. bool is_corb_enabled = true; @@ -844,6 +863,10 @@ interface NetworkContextClient { // Notification that a trust anchor was used for the given user. [EnableIf=is_chromeos] OnTrustAnchorUsed(); + + // Notification that a report was enqueued in the SCT auditing cache. + [EnableIf=is_ct_supported] + OnSCTReportReady(string cache_key); }; // Represents a distinct context for making network requests, with its own @@ -1183,6 +1206,11 @@ interface NetworkContext { // performance impact because of the extra process hops, so use should be // minimized. // + // Requests from a system context should set |process_id| to + // |kBrowserProcessId|. In that case |render_frame_id| MUST be zero. These + // values will still be passed to |OnSSLCertificateError| as needed but no + // throttling will be performed for such WebSockets. + // // Detect mojo connection errors on |handshake_client| until the connection // is established. // Do *NOT* interpret mojo connection errors on |auth_handler| and @@ -1199,6 +1227,7 @@ interface NetworkContext { int32 render_frame_id, url.mojom.Origin origin, uint32 options, + MutableNetworkTrafficAnnotationTag traffic_annotation, pending_remote<WebSocketHandshakeClient> handshake_client, pending_remote<AuthenticationHandler>? auth_handler, pending_remote<TrustedHeaderClient>? header_client); @@ -1322,11 +1351,6 @@ interface NetworkContext { url.mojom.Origin source_origin, array<CorsOriginPattern> allow_patterns, array<CorsOriginPattern> block_patterns) => (); - // Sets extra CORS safelisted request headers names dynamically. Headers need - // to be in lower case. - SetCorsExtraSafelistedRequestHeaderNames( - array<string> cors_extra_safelisted_request_header_names); - // Deletes any dynamic data stored for |host| from the transport // security state. Returns true iff an entry was deleted. // See net::TransportSecurityState::DeleteDynamicDataForHost for more detail. @@ -1376,6 +1400,16 @@ interface NetworkContext { NetworkIsolationKey network_isolation_key) => (AuthCredentials? credentials); + // Looks up the proxy authentication credentials associated with + // |proxy_server|, |auth_scheme| and |realm| in the HttpAuthCache. + // |auth_scheme| is the authentication scheme of the challenge and it's + // specified as a case-insensitive string. Unlike server credentials, proxy + // credentials are not keyed on NetworkIsolationKey. + [EnableIf=is_chromeos] + LookupProxyAuthCredentials(proxy_resolver.mojom.ProxyServer proxy_server, + string auth_scheme, string realm) + => (AuthCredentials? credentials); + [Sync] // Enables the checking of static PKP records. EnableStaticKeyPinningForTesting() => (); @@ -1400,4 +1434,8 @@ interface NetworkContext { // Gets the OriginPolicyManager associated with this network context. GetOriginPolicyManager( pending_receiver<OriginPolicyManager> origin_policy_manager); + + // Set whether SCT auditing is enabled for this NetworkContext. + [EnableIf=is_ct_supported] + SetSCTAuditingEnabled(bool enabled); }; diff --git a/chromium/services/network/public/mojom/network_service.mojom b/chromium/services/network/public/mojom/network_service.mojom index 6a016cc0eb0..b5cd71dbb98 100644 --- a/chromium/services/network/public/mojom/network_service.mojom +++ b/chromium/services/network/public/mojom/network_service.mojom @@ -74,7 +74,7 @@ interface NetworkServiceClient { int32 process_id, int32 routing_id, string devtool_request_id, - array<CookieAndLineWithStatus> cookies_with_status, + array<CookieAndLineWithAccessResult> cookies_with_access_result, array<HttpRawHeaderPair> headers, string? raw_response_headers); @@ -363,7 +363,7 @@ interface NetworkService { // - see https://www.chromium.org/flash-roadmap). AddCorbExceptionForPlugin(int32 process_id); - // Notifies |request_initiator_site_lock| enforcement code that |process_id| + // Notifies |request_initiator_origin_lock| enforcement code that |process_id| // is proxying requests on behalf of a plugin from // |allowed_request_initiator| origin. // @@ -399,6 +399,10 @@ interface NetworkService { // https://docs.google.com/document/d/1TNnya6B8pyomDK2F1R9CL3dY10OAmqWlnCxsWyOBDVQ/edit#heading=h.z52drgpfgulz. SetTrustTokenKeyCommitments(string raw_commitments) => (); + // Clears the SCT auditing cache. + [EnableIf=is_ct_supported] + ClearSCTAuditingCache(); + // Calls base::debug::DumpWithoutCrashing for the network process. // TODO(http://crbug.com/934317): Remove this once done debugging renderer // hangs. diff --git a/chromium/services/network/public/mojom/network_service_test.mojom b/chromium/services/network/public/mojom/network_service_test.mojom index 70a8683f767..895e88cd6d2 100644 --- a/chromium/services/network/public/mojom/network_service_test.mojom +++ b/chromium/services/network/public/mojom/network_service_test.mojom @@ -112,4 +112,9 @@ interface NetworkServiceTest { // Activates the specified field trial. Intended for use verifying that the // network service informs the main process when a field trial is activated. ActivateFieldTrial(string field_trial_name); + + // Instantiates a net::ScopedTestEVPolicy with the specified fingerprint and + // policy oid. + [Sync] + SetEVPolicy(array<uint8, 32> fingerprint_sha256, string policy_oid) => (); }; diff --git a/chromium/services/network/public/mojom/parsed_headers.mojom b/chromium/services/network/public/mojom/parsed_headers.mojom index 01f1bf852cb..ab10d3cfd16 100644 --- a/chromium/services/network/public/mojom/parsed_headers.mojom +++ b/chromium/services/network/public/mojom/parsed_headers.mojom @@ -4,6 +4,7 @@ module network.mojom; +import "mojo/public/mojom/base/time.mojom"; import "services/network/public/mojom/content_security_policy.mojom"; import "services/network/public/mojom/cross_origin_embedder_policy.mojom"; import "services/network/public/mojom/cross_origin_opener_policy.mojom"; @@ -15,6 +16,9 @@ struct ParsedHeaders { // The parsed Content-Security-Policy from the response headers. array<ContentSecurityPolicy> content_security_policy; + // The parsed value of the Allow-CSP-From response header. + AllowCSPFromHeaderValue? allow_csp_from; + // The parsed representation of Cross-Origin-Embedder-Policy and // Cross-Origin-Embedder-Policy-Report-Only headers. CrossOriginEmbedderPolicy cross_origin_embedder_policy; @@ -34,4 +38,7 @@ struct ParsedHeaders { // If this is present and an empty array, this means that client hints should // be disabled (if updating client hint preference is valid in this context). array<WebClientHintsType>? accept_ch; + + // Parsed Accept-CH-Lifetime value, if any. Zero if missing or on parse error. + mojo_base.mojom.TimeDelta accept_ch_lifetime; }; diff --git a/chromium/services/network/public/mojom/referrer_policy.mojom b/chromium/services/network/public/mojom/referrer_policy.mojom index b159d7cba5e..237e8f28c65 100644 --- a/chromium/services/network/public/mojom/referrer_policy.mojom +++ b/chromium/services/network/public/mojom/referrer_policy.mojom @@ -15,5 +15,4 @@ enum ReferrerPolicy { kStrictOriginWhenCrossOrigin, kSameOrigin, kStrictOrigin, - kLast = kStrictOrigin, }; diff --git a/chromium/services/network/public/mojom/ssl_config.mojom b/chromium/services/network/public/mojom/ssl_config.mojom index f95093c16ae..c2b636e9884 100644 --- a/chromium/services/network/public/mojom/ssl_config.mojom +++ b/chromium/services/network/public/mojom/ssl_config.mojom @@ -20,10 +20,6 @@ struct SSLConfig { bool sha1_local_anchors_enabled = false; bool symantec_enforcement_disabled = false; - // If true, enables TLS 1.3 downgrade hardening for connections using local - // trust anchors. (Hardening for known roots is always enabled.) - bool tls13_hardening_for_local_anchors_enabled = true; - // SSL 2.0 and 3.0 are not supported. Note these lines must be kept in sync // with net/ssl/ssl_config.cc. SSLVersion version_min = kTLS1; @@ -42,7 +38,7 @@ struct SSLConfig { // Patterns for matching hostnames to determine when to allow connection // coalescing when client certificates are also in use. Patterns follow - // the rules for host matching from the URL Blacklist filter format: + // the rules for host matching from the URL Blocklist filter format: // "example.com" matches "example.com" and all subdomains, while // ".example.net" matches exactly "example.net". Hostnames must be // canonicalized according to the rules used by GURL. diff --git a/chromium/services/network/public/mojom/trust_tokens.mojom b/chromium/services/network/public/mojom/trust_tokens.mojom index 8f599953b9f..c138d2c8b21 100644 --- a/chromium/services/network/public/mojom/trust_tokens.mojom +++ b/chromium/services/network/public/mojom/trust_tokens.mojom @@ -109,7 +109,7 @@ struct TrustTokenParams { // client-provided data). TrustTokenSignRequestData sign_request_data = kOmit; bool include_timestamp_header = false; - url.mojom.Origin? issuer; + array<url.mojom.Origin> issuers; array<string> additional_signed_headers; // "possibly_unsafe_additional_signing_data", which stores the request's diff --git a/chromium/services/network/public/mojom/url_loader.mojom b/chromium/services/network/public/mojom/url_loader.mojom index b3b921ae7a5..eaf364de661 100644 --- a/chromium/services/network/public/mojom/url_loader.mojom +++ b/chromium/services/network/public/mojom/url_loader.mojom @@ -42,7 +42,7 @@ enum RequestPriority { kHighest }; -// This enum corresponds to net::URLRequest::ReferrerPolicy. See its comments. +// This enum corresponds to net::ReferrerPolicy. See its comments. enum URLRequestReferrerPolicy { kClearReferrerOnTransitionFromSecureToInsecure, kReduceReferrerGranularityOnTransitionCrossOrigin, @@ -62,6 +62,7 @@ enum DataElementType { // Only used for Upload with Network Service as of now: kDataPipe, kChunkedDataPipe, + kReadOnceStream, kRawFile, // Used for Upload when Network Service is disabled: @@ -150,7 +151,7 @@ struct URLRequest { // // See also: // - |isolated_world_origin| - // - URLLoaderFactoryParams::request_initiator_site_lock + // - URLLoaderFactoryParams::request_initiator_origin_lock url.mojom.Origin? request_initiator; // If this is a subresource request initiated from an isolated world (e.g. diff --git a/chromium/services/network/public/mojom/url_loader_factory.mojom b/chromium/services/network/public/mojom/url_loader_factory.mojom index 8fecf02d4ff..b141eff3d63 100644 --- a/chromium/services/network/public/mojom/url_loader_factory.mojom +++ b/chromium/services/network/public/mojom/url_loader_factory.mojom @@ -8,27 +8,48 @@ import "services/network/public/mojom/mutable_network_traffic_annotation_tag.moj import "services/network/public/mojom/url_loader.mojom"; const uint32 kURLLoadOptionNone = 0; + // Sends the net::SSLInfo struct in OnReceiveResponse. const uint32 kURLLoadOptionSendSSLInfoWithResponse = 1; + // Enables mime sniffing. const uint32 kURLLoadOptionSniffMimeType = 2; + // Indicates that execution is blocking on the completion of the request. const uint32 kURLLoadOptionSynchronous = 4; + // Sends the net::SSLInfo struct in OnComplete when the connection had a major // certificate error. const uint32 kURLLoadOptionSendSSLInfoForCertificateError = 8; + // Uses the header client set in URLLoaderFactoryParams for this request. const uint32 kURLLoadOptionUseHeaderClient = 16; + // Disallow the request from sending cookies. Disallow the response from writing // cookies. const uint32 kURLLoadOptionBlockAllCookies = 32; + // Similar to |kURLLoadOptionBlockAllCookies|, but only for third party cookies. const uint32 kURLLoadOptionBlockThirdPartyCookies = 64; + // This request is for CORS preflight. This is used in the network service. // This is set and used only in the network service, no callsites outside the // service must set this. const uint32 kURLLoadOptionAsCorsPreflight = 128; +// Specifies that the request must not be made to a non-public IP address. This +// option is needed even with CORS-RFC1918 because CORS-RFC1918 is not enabled +// by default, and sometimes the client security state is not known as the time +// of the request, which skips CORS-RFC1918. +// +// This option is supported by the network service, but might not be supported +// by other URLLoaderFactory implementations. +// +// TODO(crbug.com/1115776): This option can be removed if CORS-RFC1918 ships +// without a feature flag, and additionally it changes to block requests by +// default if the security state is unknown. +const uint32 kURLLoadOptionBlockLocalRequest = 256; + // URLLoaderFactory is an interface for requesting URLs. It creates URLLoader // instances. One URLLoader instance can load one URL. interface URLLoaderFactory { diff --git a/chromium/services/network/quic_transport.cc b/chromium/services/network/quic_transport.cc index 0ef85a5b839..8a06b3ebea3 100644 --- a/chromium/services/network/quic_transport.cc +++ b/chromium/services/network/quic_transport.cc @@ -148,7 +148,10 @@ class QuicTransport::Stream final { MayDisposeLater(); } - ~Stream() { transport_->transport_->session()->CloseStream(id_); } + ~Stream() { + transport_->transport_->session()->ResetStream( + id_, quic::QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED); + } private: using ArmingPolicy = mojo::SimpleWatcher::ArmingPolicy; @@ -506,13 +509,15 @@ void QuicTransport::OnIncomingBidirectionalStreamAvailable() { sizeof(options), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 256 * 1024}; if (mojo::CreateDataPipe(&options, &writable_for_outgoing, &readable_for_outgoing) != MOJO_RESULT_OK) { - transport_->session()->CloseStream(stream->id()); + transport_->session()->ResetStream( + stream->id(), quic::QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED); // TODO(yhirano): Error the entire connection. return; } if (mojo::CreateDataPipe(&options, &writable_for_incoming, &readable_for_incoming) != MOJO_RESULT_OK) { - transport_->session()->CloseStream(stream->id()); + transport_->session()->ResetStream( + stream->id(), quic::QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED); // TODO(yhirano): Error the entire connection. return; } @@ -547,7 +552,8 @@ void QuicTransport::OnIncomingUnidirectionalStreamAvailable() { sizeof(options), MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 256 * 1024}; if (mojo::CreateDataPipe(&options, &writable_for_incoming, &readable_for_incoming) != MOJO_RESULT_OK) { - transport_->session()->CloseStream(stream->id()); + transport_->session()->ResetStream( + stream->id(), quic::QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED); // TODO(yhirano): Error the entire connection. return; } diff --git a/chromium/services/network/resource_scheduler/OWNERS b/chromium/services/network/resource_scheduler/OWNERS index 6fa38eea7ea..bd5f1d8a351 100644 --- a/chromium/services/network/resource_scheduler/OWNERS +++ b/chromium/services/network/resource_scheduler/OWNERS @@ -1,4 +1,4 @@ tbansal@chromium.org -dougarnett@chromium.org +ryansturm@chromium.org # COMPONENT: Internals>Network>NetworkQuality
\ No newline at end of file diff --git a/chromium/services/network/restricted_cookie_manager.cc b/chromium/services/network/restricted_cookie_manager.cc index 888cc78e436..99cbccd4443 100644 --- a/chromium/services/network/restricted_cookie_manager.cc +++ b/chromium/services/network/restricted_cookie_manager.cc @@ -20,6 +20,7 @@ #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "net/cookies/cookie_access_result.h" #include "net/cookies/cookie_constants.h" #include "net/cookies/cookie_options.h" #include "net/cookies/cookie_store.h" @@ -84,8 +85,9 @@ net::CookieOptions MakeOptionsForGet( return options; } -void MarkSameSiteCompatPairs(std::vector<net::CookieWithStatus>& cookie_list, - const net::CookieOptions& options) { +void MarkSameSiteCompatPairs( + std::vector<net::CookieWithAccessResult>& cookie_list, + const net::CookieOptions& options) { // If the context is same-site then there cannot be any SameSite-by-default // warnings, so the compat pair warning is irrelevant. if (options.same_site_cookie_context().GetContextForCookieInclusion() > @@ -100,9 +102,9 @@ void MarkSameSiteCompatPairs(std::vector<net::CookieWithStatus>& cookie_list, for (size_t j = i + 1; j < cookie_list.size(); ++j) { const net::CanonicalCookie& c2 = cookie_list[j].cookie; if (net::cookie_util::IsSameSiteCompatPair(c1, c2, options)) { - cookie_list[i].status.AddWarningReason( + cookie_list[i].access_result.status.AddWarningReason( net::CookieInclusionStatus::WARN_SAMESITE_COMPAT_PAIR); - cookie_list[j].status.AddWarningReason( + cookie_list[j].access_result.status.AddWarningReason( net::CookieInclusionStatus::WARN_SAMESITE_COMPAT_PAIR); } } @@ -151,7 +153,8 @@ class RestrictedCookieManager::Listener : public base::LinkNode<Listener> { void OnCookieChange(const net::CookieChangeInfo& change) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!change.cookie - .IncludeForRequestURL(url_, options_, change.access_semantics) + .IncludeForRequestURL(url_, options_, + change.access_result.access_semantics) .status.IsInclude()) { return; } @@ -269,20 +272,18 @@ void RestrictedCookieManager::CookieListToGetAllForUrlCallback( url, site_for_cookies.RepresentativeUrl(), top_frame_origin); std::vector<net::CookieWithAccessResult> result; - std::vector<net::CookieWithStatus> result_with_status; + std::vector<net::CookieWithAccessResult> on_cookies_accessed_result; // TODO(https://crbug.com/977040): Remove once samesite tightening up is // rolled out. - // |result_with_status| is populated with excluded cookies here based on - // warnings present before WARN_SAMESITE_COMPAT_PAIR can be applied by + // |on_cookies_accessed_result| is populated with excluded cookies here based + // on warnings present before WARN_SAMESITE_COMPAT_PAIR can be applied by // MarkSameSiteCompatPairs(). This is ok because WARN_SAMESITE_COMPAT_PAIR is // irrelevant unless WARN_SAMESITE_UNSPECIFIED_CROSS_SITE_CONTEXT is already // present. for (const auto& cookie_and_access_result : excluded_cookies) { if (cookie_and_access_result.access_result.status.ShouldWarn()) { - result_with_status.push_back( - {cookie_and_access_result.cookie, - cookie_and_access_result.access_result.status}); + on_cookies_accessed_result.push_back(cookie_and_access_result); } } @@ -293,7 +294,7 @@ void RestrictedCookieManager::CookieListToGetAllForUrlCallback( // TODO(https://crbug.com/993843): Use the statuses passed in |cookie_list|. for (const net::CookieWithAccessResult& cookie_item : cookie_list) { const net::CanonicalCookie& cookie = cookie_item.cookie; - net::CookieInclusionStatus status = cookie_item.access_result.status; + net::CookieAccessResult access_result = cookie_item.access_result; const std::string& cookie_name = cookie.Name(); if (match_type == mojom::CookieMatchType::EQUALS) { @@ -309,22 +310,22 @@ void RestrictedCookieManager::CookieListToGetAllForUrlCallback( } if (blocked) { - status.AddExclusionReason( + access_result.status.AddExclusionReason( net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES); } else { result.push_back(cookie_item); } - result_with_status.push_back({cookie, status}); + on_cookies_accessed_result.push_back({cookie, access_result}); } if (cookie_observer_) { - // Mark the CookieInclusionStatuses of items in |result_with_status| if they - // are part of a presumed SameSite compatibility pair. - MarkSameSiteCompatPairs(result_with_status, net_options); + // Mark the CookieInclusionStatuses of items in |result_with_access_result| + // if they are part of a presumed SameSite compatibility pair. + MarkSameSiteCompatPairs(on_cookies_accessed_result, net_options); cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New( mojom::CookieAccessDetails::Type::kRead, url, site_for_cookies, - result_with_status, base::nullopt)); + on_cookies_accessed_result, base::nullopt)); } if (blocked) { @@ -343,7 +344,8 @@ void RestrictedCookieManager::SetCanonicalCookie( const url::Origin& top_frame_origin, SetCanonicalCookieCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin)) { + if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin, + &cookie)) { std::move(callback).Run(false); return; } @@ -363,24 +365,13 @@ void RestrictedCookieManager::SetCanonicalCookie( status.AddExclusionReason( net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN); - // Don't allow setting cookies on other domains. - // TODO(crbug.com/996786): This should never happen. This should eventually - // result in a renderer kill, but for now just log metrics. - bool domain_match = cookie.IsDomainMatch(url.host()); - if (!domain_match) - status.AddExclusionReason( - net::CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH); - UMA_HISTOGRAM_BOOLEAN( - "Net.RestrictedCookieManager.SetCanonicalCookieDomainMatch", - domain_match); - if (!status.IsInclude()) { if (cookie_observer_) { - std::vector<net::CookieWithStatus> result_with_status = { - {cookie, status}}; + std::vector<net::CookieWithAccessResult> result_with_access_result = { + {cookie, net::CookieAccessResult(status)}}; cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New( mojom::CookieAccessDetails::Type::kChange, url, site_for_cookies, - result_with_status, base::nullopt)); + result_with_access_result, base::nullopt)); } std::move(callback).Run(false); return; @@ -420,22 +411,22 @@ void RestrictedCookieManager::SetCanonicalCookieResult( const net::CanonicalCookie& cookie, const net::CookieOptions& net_options, SetCanonicalCookieCallback user_callback, - net::CookieInclusionStatus status) { - std::vector<net::CookieWithStatus> notify; + net::CookieAccessResult access_result) { + std::vector<net::CookieWithAccessResult> notify; // TODO(https://crbug.com/977040): Only report pure INCLUDE once samesite // tightening up is rolled out. - DCHECK(!status.HasExclusionReason( + DCHECK(!access_result.status.HasExclusionReason( net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES)); - if (status.IsInclude() || status.ShouldWarn()) { + if (access_result.status.IsInclude() || access_result.status.ShouldWarn()) { if (cookie_observer_) { - notify.push_back({cookie, status}); + notify.push_back({cookie, access_result}); cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New( mojom::CookieAccessDetails::Type::kChange, url, site_for_cookies, notify, base::nullopt)); } } - std::move(user_callback).Run(status.IsInclude()); + std::move(user_callback).Run(access_result.status.IsInclude()); } void RestrictedCookieManager::AddChangeListener( @@ -540,7 +531,8 @@ void RestrictedCookieManager::RemoveChangeListener(Listener* listener) { bool RestrictedCookieManager::ValidateAccessToCookiesAt( const GURL& url, const net::SiteForCookies& site_for_cookies, - const url::Origin& top_frame_origin) { + const url::Origin& top_frame_origin, + const net::CanonicalCookie* cookie_being_set) { if (origin_.opaque()) { mojo::ReportBadMessage("Access is denied in this context"); return false; @@ -561,6 +553,12 @@ bool RestrictedCookieManager::ValidateAccessToCookiesAt( UMA_HISTOGRAM_BOOLEAN("Net.RestrictedCookieManager.TopFrameOriginOK", top_frame_origin_ok); + // Don't allow setting cookies on other domains. See crbug.com/996786. + if (cookie_being_set && !cookie_being_set->IsDomainMatch(url.host())) { + mojo::ReportBadMessage("Setting cookies on other domains is disallowed."); + return false; + } + if (origin_.IsSameOriginWith(url::Origin::Create(url))) return true; diff --git a/chromium/services/network/restricted_cookie_manager.h b/chromium/services/network/restricted_cookie_manager.h index 1b0b50812e5..9c89588b80b 100644 --- a/chromium/services/network/restricted_cookie_manager.h +++ b/chromium/services/network/restricted_cookie_manager.h @@ -122,7 +122,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) RestrictedCookieManager const net::CanonicalCookie& cookie, const net::CookieOptions& net_options, SetCanonicalCookieCallback user_callback, - net::CookieInclusionStatus status); + net::CookieAccessResult access_result); // Called when the Mojo pipe associated with a listener is closed. void RemoveChangeListener(Listener* listener); @@ -132,11 +132,16 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) RestrictedCookieManager // Returns true if the access should be allowed, or false if it should be // blocked. // + // |cookie_being_set| should be non-nullptr if setting a cookie, and should be + // nullptr otherwise (getting cookies, subscribing to cookie changes). + // // If the access would not be allowed, this helper calls // mojo::ReportBadMessage(), which closes the pipe. - bool ValidateAccessToCookiesAt(const GURL& url, - const net::SiteForCookies& site_for_cookies, - const url::Origin& top_frame_origin); + bool ValidateAccessToCookiesAt( + const GURL& url, + const net::SiteForCookies& site_for_cookies, + const url::Origin& top_frame_origin, + const net::CanonicalCookie* cookie_being_set = nullptr); const mojom::RestrictedCookieManagerRole role_; net::CookieStore* const cookie_store_; diff --git a/chromium/services/network/restricted_cookie_manager_unittest.cc b/chromium/services/network/restricted_cookie_manager_unittest.cc index 36db8313971..d91c0c563ef 100644 --- a/chromium/services/network/restricted_cookie_manager_unittest.cc +++ b/chromium/services/network/restricted_cookie_manager_unittest.cc @@ -60,13 +60,13 @@ class RecordingCookieObserver : public network::mojom::CookieAccessObserver { } void OnCookiesAccessed(mojom::CookieAccessDetailsPtr details) override { - for (const auto& cookie_and_status : details->cookie_list) { + for (const auto& cookie_and_access_result : details->cookie_list) { CookieOp op; op.get = details->type == mojom::CookieAccessDetails::Type::kRead; op.url = details->url; op.site_for_cookies = details->site_for_cookies.RepresentativeUrl(); - op.cookie.push_back(cookie_and_status.cookie); - op.status = cookie_and_status.status; + op.cookie.push_back(cookie_and_access_result.cookie); + op.status = cookie_and_access_result.access_result.status; op.devtools_request_id = details->devtools_request_id; recorded_activity_.push_back(op); } @@ -204,33 +204,31 @@ class RestrictedCookieManagerTest bool SetCanonicalCookie(const net::CanonicalCookie& cookie, std::string source_scheme, bool can_modify_httponly) { - net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback; + net::ResultSavingCookieCallback<net::CookieAccessResult> callback; net::CookieOptions options; if (can_modify_httponly) options.set_include_httponly(); cookie_monster_.SetCanonicalCookieAsync( std::make_unique<net::CanonicalCookie>(cookie), net::cookie_util::SimulatedCookieSource(cookie, source_scheme), options, - base::BindOnce( - &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, - base::Unretained(&callback))); + callback.MakeCallback()); callback.WaitUntilDone(); - return callback.result().IsInclude(); + return callback.result().status.IsInclude(); } // Set a canonical cookie directly into the store. // Uses a cookie options that will succeed at setting any cookie. bool EnsureSetCanonicalCookie(const net::CanonicalCookie& cookie) { - net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback; + net::ResultSavingCookieCallback<net::CookieAccessResult> callback; cookie_monster_.SetCanonicalCookieAsync( std::make_unique<net::CanonicalCookie>(cookie), net::cookie_util::SimulatedCookieSource(cookie, "https"), net::CookieOptions::MakeAllInclusive(), base::BindOnce( - &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, + &net::ResultSavingCookieCallback<net::CookieAccessResult>::Run, base::Unretained(&callback))); callback.WaitUntilDone(); - return callback.result().IsInclude(); + return callback.result().status.IsInclude(); } // Simplified helper for SetCanonicalCookie. @@ -616,28 +614,6 @@ TEST_P(RestrictedCookieManagerTest, SetCanonicalCookieHttpOnly) { } } -TEST_P(RestrictedCookieManagerTest, SetCanonicalCookieValidateDomain) { - GURL other_site("https://not-example.com"); - auto cookie = net::CanonicalCookie::Create( - other_site, "cookie=foo;domain=not-example.com", base::Time::Now(), - base::nullopt); - ASSERT_EQ(".not-example.com", cookie->Domain()); - EXPECT_FALSE(sync_service_->SetCanonicalCookie( - *cookie, GURL("https://example.com/test"), GURL("https://example.com"), - url::Origin::Create(GURL("https://example.com")))); - ASSERT_EQ(1u, recorded_activity().size()); - EXPECT_TRUE(recorded_activity()[0].status.HasExclusionReason( - net::CookieInclusionStatus::EXCLUDE_DOMAIN_MISMATCH)); - - auto options = mojom::CookieManagerGetOptions::New(); - options->name = "cookie"; - options->match_type = mojom::CookieMatchType::EQUALS; - std::vector<net::CanonicalCookie> cookies = sync_service_->GetAllForUrl( - GURL("https://example.com/test/"), GURL("https://example.com"), - url::Origin::Create(GURL("https://example.com")), std::move(options)); - EXPECT_THAT(cookies, testing::SizeIs(0)); -} - TEST_P(RestrictedCookieManagerTest, SetCookieFromString) { EXPECT_TRUE(backend()->SetCookieFromString( GURL("https://example.com/test/"), @@ -687,6 +663,19 @@ TEST_P(RestrictedCookieManagerTest, SetCanonicalCookieFromOpaqueOrigin) { ASSERT_TRUE(received_bad_message()); } +TEST_P(RestrictedCookieManagerTest, SetCanonicalCookieWithMismatchingDomain) { + ExpectBadMessage(); + EXPECT_FALSE(sync_service_->SetCanonicalCookie( + net::CanonicalCookie( + "new-name", "new-value", "not-example.com", "/", base::Time(), + base::Time(), base::Time(), /* secure = */ true, + /* httponly = */ false, net::CookieSameSite::NO_RESTRICTION, + net::COOKIE_PRIORITY_DEFAULT), + GURL("https://example.com/test/"), GURL("https://example.com"), + url::Origin::Create(GURL("https://example.com")))); + ASSERT_TRUE(received_bad_message()); +} + TEST_P(RestrictedCookieManagerTest, SetCookieFromStringWrongOrigin) { ExpectBadMessage(); EXPECT_TRUE(backend()->SetCookieFromString( @@ -1290,21 +1279,19 @@ TEST_P(RestrictedCookieManagerTest, ChangeNotificationIncludesAccessSemantics) { base::nullopt); // Set cookie directly into the CookieMonster, using all-inclusive options. - net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback; + net::ResultSavingCookieCallback<net::CookieAccessResult> callback; cookie_monster_.SetCanonicalCookieAsync( std::move(cookie), cookie_url, net::CookieOptions::MakeAllInclusive(), - base::BindOnce( - &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, - base::Unretained(&callback))); + callback.MakeCallback()); callback.WaitUntilDone(); - ASSERT_TRUE(callback.result().IsInclude()); + ASSERT_TRUE(callback.result().status.IsInclude()); // The listener only receives the change because the cookie is legacy. listener.WaitForChange(); ASSERT_THAT(listener.observed_changes(), testing::SizeIs(1)); EXPECT_EQ(net::CookieAccessSemantics::LEGACY, - listener.observed_changes()[0].access_semantics); + listener.observed_changes()[0].access_result.access_semantics); } TEST_P(RestrictedCookieManagerTest, NoChangeNotificationForNonlegacyCookie) { @@ -1345,30 +1332,24 @@ TEST_P(RestrictedCookieManagerTest, NoChangeNotificationForNonlegacyCookie) { base::Time::Now(), base::nullopt); // Set cookies directly into the CookieMonster, using all-inclusive options. - net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback1; + net::ResultSavingCookieCallback<net::CookieAccessResult> callback1; cookie_monster_.SetCanonicalCookieAsync( std::move(unspecified_cookie), cookie_url, - net::CookieOptions::MakeAllInclusive(), - base::BindOnce( - &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, - base::Unretained(&callback1))); + net::CookieOptions::MakeAllInclusive(), callback1.MakeCallback()); callback1.WaitUntilDone(); - ASSERT_TRUE(callback1.result().IsInclude()); + ASSERT_TRUE(callback1.result().status.IsInclude()); // Listener doesn't receive notification because cookie is not included for // request URL for being unspecified and treated as lax. base::RunLoop().RunUntilIdle(); ASSERT_THAT(listener.observed_changes(), testing::SizeIs(0)); - net::ResultSavingCookieCallback<net::CookieInclusionStatus> callback2; + net::ResultSavingCookieCallback<net::CookieAccessResult> callback2; cookie_monster_.SetCanonicalCookieAsync( std::move(samesite_none_cookie), cookie_url, - net::CookieOptions::MakeAllInclusive(), - base::BindOnce( - &net::ResultSavingCookieCallback<net::CookieInclusionStatus>::Run, - base::Unretained(&callback2))); + net::CookieOptions::MakeAllInclusive(), callback2.MakeCallback()); callback2.WaitUntilDone(); - ASSERT_TRUE(callback2.result().IsInclude()); + ASSERT_TRUE(callback2.result().status.IsInclude()); // Listener only receives notification about the SameSite=None cookie. listener.WaitForChange(); @@ -1377,7 +1358,7 @@ TEST_P(RestrictedCookieManagerTest, NoChangeNotificationForNonlegacyCookie) { EXPECT_EQ("samesite_none_cookie", listener.observed_changes()[0].cookie.Name()); EXPECT_EQ(net::CookieAccessSemantics::NONLEGACY, - listener.observed_changes()[0].access_semantics); + listener.observed_changes()[0].access_result.access_semantics); } INSTANTIATE_TEST_SUITE_P( diff --git a/chromium/services/network/sct_auditing_cache.cc b/chromium/services/network/sct_auditing_cache.cc new file mode 100644 index 00000000000..c6b01f76ab2 --- /dev/null +++ b/chromium/services/network/sct_auditing_cache.cc @@ -0,0 +1,100 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/network/sct_auditing_cache.h" + +#include "base/feature_list.h" +#include "base/metrics/field_trial_params.h" +#include "base/rand_util.h" +#include "crypto/secure_hash.h" +#include "crypto/sha2.h" +#include "net/base/hash_value.h" +#include "net/base/host_port_pair.h" +#include "net/cert/ct_serialization.h" +#include "net/cert/signed_certificate_timestamp.h" +#include "net/cert/signed_certificate_timestamp_and_status.h" +#include "net/cert/x509_certificate.h" +#include "services/network/network_context.h" +#include "services/network/network_service.h" +#include "services/network/public/cpp/features.h" +#include "third_party/boringssl/src/include/openssl/pool.h" +#include "third_party/boringssl/src/include/openssl/sha.h" + +namespace network { + +SCTAuditReport::SCTAuditReport() = default; +SCTAuditReport::~SCTAuditReport() = default; +SCTAuditReport::SCTAuditReport(const SCTAuditReport& other) = default; +SCTAuditReport::SCTAuditReport(SCTAuditReport&& other) = default; + +SCTAuditingCache::SCTAuditingCache(size_t cache_size) : cache_(cache_size) {} +SCTAuditingCache::~SCTAuditingCache() = default; + +void SCTAuditingCache::MaybeEnqueueReport( + NetworkContext* context, + const net::HostPortPair& host_port_pair, + const net::X509Certificate* validated_certificate_chain, + const net::SignedCertificateTimestampAndStatusList& + signed_certificate_timestamps) { + if (!base::FeatureList::IsEnabled(features::kSCTAuditing) || + !context->is_sct_auditing_enabled()) { + return; + } + + // Generate the cache key for this report. In order to have the cache + // deduplicate reports for the same SCTs, we compute the cache key as the + // hash of the SCTs. The digest is converted to a string for use over Mojo. + SHA256_CTX ctx; + SHA256_Init(&ctx); + for (const auto& sct : signed_certificate_timestamps) { + std::string serialized_sct; + net::ct::EncodeSignedCertificateTimestamp(sct.sct, &serialized_sct); + SHA256_Update(&ctx, serialized_sct.data(), serialized_sct.size()); + } + net::SHA256HashValue cache_key; + SHA256_Final(reinterpret_cast<uint8_t*>(&cache_key), &ctx); + + // Check if the SCTs are already in the cache. This will update the last seen + // time if they are present in the cache. + auto it = cache_.Get(cache_key); + if (it != cache_.end()) + return; + + // Insert SCTs into cache. + // TODO(crbug.com/1082860): Construct the proto object directly and store that + // in the cache instead of this intermediate form, once the proto is added. + auto report = std::make_unique<SCTAuditReport>(); + report->time_seen = base::Time::Now(); + report->host_port_pair = host_port_pair; + + // GetPEMEncodedChain() can fail, but we still want to enqueue the report for + // the SCTs (in that case, |report->certificate_chain| is not guaranteed to + // be valid). + report->certificate_chain = std::vector<std::string>(); + validated_certificate_chain->GetPEMEncodedChain(&report->certificate_chain); + + for (const auto& sct : signed_certificate_timestamps) { + report->sct_list.push_back(sct); + } + + cache_.Put(cache_key, std::move(report)); + + double sampling_rate = features::kSCTAuditingSamplingRate.Get(); + if (base::RandDouble() > sampling_rate) + return; + + context->client()->OnSCTReportReady(net::HashValue(cache_key).ToString()); +} + +SCTAuditReport* SCTAuditingCache::GetPendingReport( + const net::SHA256HashValue& cache_key) { + NOTIMPLEMENTED(); + return nullptr; +} + +void SCTAuditingCache::ClearCache() { + cache_.Clear(); +} + +} // namespace network diff --git a/chromium/services/network/sct_auditing_cache.h b/chromium/services/network/sct_auditing_cache.h new file mode 100644 index 00000000000..1ddfdd56e40 --- /dev/null +++ b/chromium/services/network/sct_auditing_cache.h @@ -0,0 +1,87 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_NETWORK_SCT_AUDITING_CACHE_H_ +#define SERVICES_NETWORK_SCT_AUDITING_CACHE_H_ + +#include <string> +#include <vector> + +#include "base/component_export.h" +#include "base/containers/mru_cache.h" +#include "base/time/time.h" +#include "net/base/hash_value.h" +#include "net/base/host_port_pair.h" +#include "net/cert/sct_auditing_delegate.h" +#include "net/cert/signed_certificate_timestamp_and_status.h" + +namespace net { +class X509Certificate; +} + +namespace network { +class NetworkContext; + +struct SCTAuditReport { + SCTAuditReport(); + ~SCTAuditReport(); + + SCTAuditReport(const SCTAuditReport& other); + SCTAuditReport& operator=(const SCTAuditReport& other) = default; + SCTAuditReport(SCTAuditReport&& other); + SCTAuditReport& operator=(SCTAuditReport&& other) = default; + + base::Time time_seen; + net::HostPortPair host_port_pair; + std::vector<std::string> certificate_chain; + std::vector<net::SignedCertificateTimestampAndStatus> sct_list; +}; + +// SCTAuditingCache tracks SCTs seen during CT verification. The cache supports +// a configurable sample rate to reduce load, and deduplicates SCTs seen more +// than once. The cache evicts least-recently-used entries after it reaches its +// capacity. +// +// A single SCTAuditingCache should be shared among all contexts that want to +// deduplicate reports and use a single sampling mechanism. Currently, one +// SCTAuditingCache is created and owned by the NetworkService and shared +// across all NetworkContexts. +class COMPONENT_EXPORT(NETWORK_SERVICE) SCTAuditingCache { + public: + explicit SCTAuditingCache(size_t cache_size); + ~SCTAuditingCache(); + + SCTAuditingCache(const SCTAuditingCache&) = delete; + SCTAuditingCache& operator=(const SCTAuditingCache&) = delete; + + // Creates a report containing the details about the connection context and + // SCTs and adds it to the cache if the SCTs are not already in the + // cache. If the SCTs were not already in the cache, a random sample is drawn + // to determine whether to notify the NetworkContextClient (and thus send a + // report). This means we sample a subset of *certificates* rather than a + // subset of *connections*. If a new entry is sampled, the associated + // NetworkContextClient is notified. + void MaybeEnqueueReport( + NetworkContext* context, + const net::HostPortPair& host_port_pair, + const net::X509Certificate* validated_certificate_chain, + const net::SignedCertificateTimestampAndStatusList& + signed_certificate_timestamps); + + SCTAuditReport* GetPendingReport(const net::SHA256HashValue& cache_key); + + void ClearCache(); + + base::MRUCache<net::SHA256HashValue, std::unique_ptr<SCTAuditReport>>* + GetCacheForTesting() { + return &cache_; + } + + private: + base::MRUCache<net::SHA256HashValue, std::unique_ptr<SCTAuditReport>> cache_; +}; + +} // namespace network + +#endif // SERVICES_NETWORK_SCT_AUDITING_CACHE_H_ diff --git a/chromium/services/network/sct_auditing_cache_unittest.cc b/chromium/services/network/sct_auditing_cache_unittest.cc new file mode 100644 index 00000000000..b1a86e6805e --- /dev/null +++ b/chromium/services/network/sct_auditing_cache_unittest.cc @@ -0,0 +1,272 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/network/sct_auditing_cache.h" + +#include "base/feature_list.h" +#include "base/memory/scoped_refptr.h" +#include "base/metrics/field_trial_params.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "net/base/host_port_pair.h" +#include "net/cert/sct_status_flags.h" +#include "net/cert/signed_certificate_timestamp.h" +#include "net/cert/signed_certificate_timestamp_and_status.h" +#include "net/test/cert_test_util.h" +#include "net/test/test_data_directory.h" +#include "services/network/network_context.h" +#include "services/network/network_service.h" +#include "services/network/public/cpp/features.h" +#include "services/network/test/test_network_context_client.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace network { + +namespace { + +base::test::ScopedFeatureList::FeatureAndParams probability_zero{ + network::features::kSCTAuditing, + {{network::features::kSCTAuditingSamplingRate.name, "0.0"}}}; +base::test::ScopedFeatureList::FeatureAndParams probability_one{ + network::features::kSCTAuditing, + {{network::features::kSCTAuditingSamplingRate.name, "1.0"}}}; + +class SCTAuditingCacheTest : public testing::Test { + public: + SCTAuditingCacheTest() + : network_service_(NetworkService::CreateForTesting()) {} + ~SCTAuditingCacheTest() override = default; + + SCTAuditingCacheTest(const SCTAuditingCacheTest&) = delete; + SCTAuditingCacheTest& operator=(const SCTAuditingCacheTest&) = delete; + + void SetUp() override { + InitNetworkContext(); + network_context_->SetIsSCTAuditingEnabledForTesting(true); + chain_ = + net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem"); + ASSERT_TRUE(chain_.get()); + } + + protected: + void InitNetworkContext() { + network_context_ = std::make_unique<NetworkContext>( + network_service_.get(), + network_context_remote_.BindNewPipeAndPassReceiver(), + mojom::NetworkContextParams::New()); + + // A NetworkContextClient is needed for embedder notifications to work. + mojo::PendingRemote<network::mojom::NetworkContextClient> + network_context_client_remote; + network_context_client_ = + std::make_unique<network::TestNetworkContextClient>( + network_context_client_remote.InitWithNewPipeAndPassReceiver()); + network_context_->SetClient(std::move(network_context_client_remote)); + } + + base::test::TaskEnvironment task_environment_{ + base::test::TaskEnvironment::MainThreadType::IO}; + std::unique_ptr<NetworkService> network_service_; + std::unique_ptr<NetworkContext> network_context_; + std::unique_ptr<network::mojom::NetworkContextClient> network_context_client_; + + scoped_refptr<net::X509Certificate> chain_; + + // Stores the mojo::Remote<mojom::NetworkContext> of the most recently created + // NetworkContext. + mojo::Remote<mojom::NetworkContext> network_context_remote_; +}; + +// Constructs a net::SignedCertificateTimestampAndStatus with the given +// information and appends it to |sct_list|. +void MakeTestSCTAndStatus( + net::ct::SignedCertificateTimestamp::Origin origin, + const std::string& extensions, + const std::string& signature_data, + const base::Time& timestamp, + net::ct::SCTVerifyStatus status, + net::SignedCertificateTimestampAndStatusList* sct_list) { + scoped_refptr<net::ct::SignedCertificateTimestamp> sct( + new net::ct::SignedCertificateTimestamp()); + sct->version = net::ct::SignedCertificateTimestamp::V1; + + // The particular value of the log ID doesn't matter; it just has to be the + // correct length. + const unsigned char kTestLogId[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; + const std::string log_id(reinterpret_cast<const char*>(kTestLogId), + sizeof(kTestLogId)); + sct->log_id = log_id; + + sct->extensions = extensions; + sct->timestamp = timestamp; + sct->signature.signature_data = signature_data; + sct->origin = origin; + sct_list->push_back(net::SignedCertificateTimestampAndStatus(sct, status)); +} + +} // namespace + +// Test that if auditing is disabled on the NetworkContext, no reports are +// cached. +TEST_F(SCTAuditingCacheTest, NoReportsCachedWhenAuditingDisabled) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeaturesAndParameters({probability_one}, {}); + SCTAuditingCache cache(10); + + network_context_->SetIsSCTAuditingEnabledForTesting(false); + + const net::HostPortPair host_port_pair("example.com", 443); + net::SignedCertificateTimestampAndStatusList sct_list; + MakeTestSCTAndStatus(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED, + "extensions1", "signature1", base::Time::Now(), + net::ct::SCT_STATUS_LOG_UNKNOWN, &sct_list); + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair, chain_.get(), + sct_list); + + ASSERT_EQ(0u, cache.GetCacheForTesting()->size()); +} + +// Test that inserting and retrieving a report works. +TEST_F(SCTAuditingCacheTest, InsertAndRetrieveReport) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeaturesAndParameters({probability_one}, {}); + SCTAuditingCache cache(10); + + const net::HostPortPair host_port_pair("example.com", 443); + net::SignedCertificateTimestampAndStatusList sct_list; + MakeTestSCTAndStatus(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED, + "extensions1", "signature1", base::Time::Now(), + net::ct::SCT_STATUS_LOG_UNKNOWN, &sct_list); + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair, chain_.get(), + sct_list); + + ASSERT_EQ(1u, cache.GetCacheForTesting()->size()); +} + +// Tests that old entries are evicted when the cache is full. +TEST_F(SCTAuditingCacheTest, EvictLRUAfterCacheFull) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeaturesAndParameters({probability_one}, {}); + SCTAuditingCache cache(2); + + const net::HostPortPair host_port_pair1("example1.com", 443); + const net::HostPortPair host_port_pair2("example2.com", 443); + const net::HostPortPair host_port_pair3("example3.com", 443); + + { + net::SignedCertificateTimestampAndStatusList sct_list; + MakeTestSCTAndStatus(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED, + "extensions1", "signature1", base::Time::Now(), + net::ct::SCT_STATUS_LOG_UNKNOWN, &sct_list); + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair1, + chain_.get(), sct_list); + ASSERT_EQ(1u, cache.GetCacheForTesting()->size()); + } + + { + net::SignedCertificateTimestampAndStatusList sct_list; + MakeTestSCTAndStatus(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED, + "extensions1", "signature2", base::Time::Now(), + net::ct::SCT_STATUS_LOG_UNKNOWN, &sct_list); + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair2, + chain_.get(), sct_list); + ASSERT_EQ(2u, cache.GetCacheForTesting()->size()); + } + + // Cache is now full, so the first entry (for "example1.com") should no longer + // be in the cache after inserting a third entry. + { + net::SignedCertificateTimestampAndStatusList sct_list; + MakeTestSCTAndStatus(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED, + "extensions1", "signature3", base::Time::Now(), + net::ct::SCT_STATUS_LOG_UNKNOWN, &sct_list); + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair3, + chain_.get(), sct_list); + ASSERT_EQ(2u, cache.GetCacheForTesting()->size()); + for (const auto& entry : *cache.GetCacheForTesting()) { + ASSERT_NE("example1.com", entry.second->host_port_pair.host()); + } + } +} + +// Tests that a new report gets dropped if the same SCTs are already in the +// cache. +TEST_F(SCTAuditingCacheTest, ReportWithSameSCTsDeduplicated) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeaturesAndParameters({probability_one}, {}); + SCTAuditingCache cache(10); + + const net::HostPortPair host_port_pair1("example.com", 443); + const net::HostPortPair host_port_pair2("example.org", 443); + + net::SignedCertificateTimestampAndStatusList sct_list; + MakeTestSCTAndStatus(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED, + "extensions1", "signature1", base::Time::Now(), + net::ct::SCT_STATUS_LOG_UNKNOWN, &sct_list); + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair1, + chain_.get(), sct_list); + + ASSERT_EQ(1u, cache.GetCacheForTesting()->size()); + + // Enqueuing the same SCTs won't cause a new report to be added to the queue + // (even if the connection origin is different). + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair2, + chain_.get(), sct_list); + ASSERT_EQ(1u, cache.GetCacheForTesting()->size()); +} + +// When a report gets deduplicated, the existing entry should have its last-seen +// time bumped up. +TEST_F(SCTAuditingCacheTest, DeduplicationUpdatesLastSeenTime) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeaturesAndParameters({probability_one}, {}); + SCTAuditingCache cache(2); + + const net::HostPortPair host_port_pair1("example1.com", 443); + const net::HostPortPair host_port_pair2("example2.com", 443); + const net::HostPortPair host_port_pair3("example3.com", 443); + + // Fill the cache with two reports. + net::SignedCertificateTimestampAndStatusList sct_list1; + MakeTestSCTAndStatus(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED, + "extensions1", "signature1", base::Time::Now(), + net::ct::SCT_STATUS_LOG_UNKNOWN, &sct_list1); + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair1, + chain_.get(), sct_list1); + + net::SignedCertificateTimestampAndStatusList sct_list2; + MakeTestSCTAndStatus(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED, + "extensions2", "signature2", base::Time::Now(), + net::ct::SCT_STATUS_LOG_UNKNOWN, &sct_list2); + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair2, + chain_.get(), sct_list2); + + EXPECT_EQ(2u, cache.GetCacheForTesting()->size()); + + // Try to enqueue the report for "example1.com" again. It should be deduped. + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair1, + chain_.get(), sct_list1); + EXPECT_EQ(2u, cache.GetCacheForTesting()->size()); + + // If we enqueue a new report causing the cache size limit to be exceeded, + // "example1.com" should be the most-recent due to getting updated during + // deduping, and "example2.com" should get evicted instead. + net::SignedCertificateTimestampAndStatusList sct_list3; + MakeTestSCTAndStatus(net::ct::SignedCertificateTimestamp::SCT_EMBEDDED, + "extensions3", "signature3", base::Time::Now(), + net::ct::SCT_STATUS_LOG_UNKNOWN, &sct_list3); + cache.MaybeEnqueueReport(network_context_.get(), host_port_pair3, + chain_.get(), sct_list3); + + EXPECT_EQ(2u, cache.GetCacheForTesting()->size()); + for (const auto& entry : *cache.GetCacheForTesting()) { + ASSERT_NE("example2.com", entry.second->host_port_pair.host()); + } +} + +} // namespace network diff --git a/chromium/services/network/sec_header_helpers.cc b/chromium/services/network/sec_header_helpers.cc index a02160b2170..7e64d9cc575 100644 --- a/chromium/services/network/sec_header_helpers.cc +++ b/chromium/services/network/sec_header_helpers.cc @@ -78,7 +78,7 @@ void SetSecFetchSiteHeader( const mojom::URLLoaderFactoryParams& factory_params) { SecFetchSiteValue header_value; url::Origin initiator = GetTrustworthyInitiator( - factory_params.request_initiator_site_lock, request->initiator()); + factory_params.request_initiator_origin_lock, request->initiator()); // Browser-initiated requests with no initiator origin, and // privileged requests initiated from a "non-webby" context will send diff --git a/chromium/services/network/session_cleanup_cookie_store.h b/chromium/services/network/session_cleanup_cookie_store.h index 7e9a12ea700..46c6a84dc3b 100644 --- a/chromium/services/network/session_cleanup_cookie_store.h +++ b/chromium/services/network/session_cleanup_cookie_store.h @@ -18,6 +18,7 @@ #include "net/cookies/cookie_monster.h" #include "net/extras/sqlite/sqlite_persistent_cookie_store.h" #include "net/log/net_log_with_source.h" +#include "services/network/public/cpp/session_cookie_delete_predicate.h" namespace net { class CanonicalCookie; @@ -26,16 +27,12 @@ class CanonicalCookie; namespace network { // Implements a PersistentCookieStore that keeps an in-memory map of cookie -// origins, and allows deletion of cookies using the DeleteCookiePredicate. This -// is used to clear cookies with session-only policy at the end of a session. +// origins, and allows deletion of cookies using the +// network::DeleteCookiePredicate. This is used to clear cookies with +// session-only policy at the end of a session. class COMPONENT_EXPORT(NETWORK_SERVICE) SessionCleanupCookieStore : public net::CookieMonster::PersistentCookieStore { public: - // Returns true if the cookie associated with the domain and is_https status - // should be deleted. - using DeleteCookiePredicate = - base::RepeatingCallback<bool(const std::string&, bool)>; - using CookiesPerOriginMap = std::map<net::SQLitePersistentCookieStore::CookieOrigin, size_t>; diff --git a/chromium/services/network/ssl_config_service_mojo.cc b/chromium/services/network/ssl_config_service_mojo.cc index e874fa7c3fc..247d8dfae4b 100644 --- a/chromium/services/network/ssl_config_service_mojo.cc +++ b/chromium/services/network/ssl_config_service_mojo.cc @@ -5,7 +5,7 @@ #include "services/network/ssl_config_service_mojo.h" #include "base/strings/string_piece.h" -#include "base/strings/string_piece_forward.h" +#include "base/strings/string_util.h" #include "mojo/public/cpp/bindings/type_converter.h" #include "services/network/legacy_tls_config_distributor.h" #include "services/network/ssl_config_type_converter.h" @@ -24,7 +24,7 @@ bool IsSubdomain(const base::StringPiece hostname, if (hostname.length() <= (pattern.length() + 1)) { return false; } - if (!hostname.ends_with(pattern)) { + if (!base::EndsWith(hostname, pattern)) { return false; } return hostname[hostname.length() - pattern.length() - 1] == '.'; diff --git a/chromium/services/network/ssl_config_service_mojo_unittest.cc b/chromium/services/network/ssl_config_service_mojo_unittest.cc index 8679184550f..2b85ba4a52c 100644 --- a/chromium/services/network/ssl_config_service_mojo_unittest.cc +++ b/chromium/services/network/ssl_config_service_mojo_unittest.cc @@ -439,16 +439,6 @@ TEST_F(NetworkServiceSSLConfigServiceTest, RunConversionTests(*mojo_config, expected_net_config); } -TEST_F(NetworkServiceSSLConfigServiceTest, InitialConfigTLS13Hardening) { - net::SSLContextConfig expected_net_config; - expected_net_config.tls13_hardening_for_local_anchors_enabled = false; - - mojom::SSLConfigPtr mojo_config = mojom::SSLConfig::New(); - mojo_config->tls13_hardening_for_local_anchors_enabled = false; - - RunConversionTests(*mojo_config, expected_net_config); -} - TEST_F(NetworkServiceSSLConfigServiceTest, CanShareConnectionWithClientCerts) { // Create a default NetworkContext and test that // CanShareConnectionWithClientCerts returns false. diff --git a/chromium/services/network/ssl_config_type_converter.cc b/chromium/services/network/ssl_config_type_converter.cc index 3fc0c4db592..7d09bb5cd51 100644 --- a/chromium/services/network/ssl_config_type_converter.cc +++ b/chromium/services/network/ssl_config_type_converter.cc @@ -37,8 +37,6 @@ net::SSLContextConfig MojoSSLConfigToSSLContextConfig( DCHECK_LE(net_config.version_min, net_config.version_max); net_config.disabled_cipher_suites = mojo_config->disabled_cipher_suites; - net_config.tls13_hardening_for_local_anchors_enabled = - mojo_config->tls13_hardening_for_local_anchors_enabled; return net_config; } diff --git a/chromium/services/network/tcp_bound_socket_unittest.cc b/chromium/services/network/tcp_bound_socket_unittest.cc index facd307dd87..c6800ff89a9 100644 --- a/chromium/services/network/tcp_bound_socket_unittest.cc +++ b/chromium/services/network/tcp_bound_socket_unittest.cc @@ -226,7 +226,7 @@ TEST_F(TCPBoundSocketTest, BindError) { // // Don't run on Apple platforms because this pattern ends in a connect timeout // on OSX (after 25+ seconds) instead of connection refused. -#if !defined(OS_MACOSX) && !defined(OS_IOS) +#if !defined(OS_APPLE) TEST_F(TCPBoundSocketTest, ConnectError) { mojo::Remote<mojom::TCPBoundSocket> bound_socket1; net::IPEndPoint bound_address1; @@ -248,7 +248,7 @@ TEST_F(TCPBoundSocketTest, ConnectError) { &connected_socket, mojo::NullRemote(), &client_socket_receive_handle, &client_socket_send_handle)); } -#endif // !defined(OS_MACOSX) && !defined(OS_IOS) +#endif // !defined(OS_APPLE) // Test listen failure. @@ -258,7 +258,7 @@ TEST_F(TCPBoundSocketTest, ConnectError) { // // Apple platforms don't allow binding multiple TCP sockets to the same port // even with SO_REUSEADDR enabled. -#if !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_IOS) +#if !defined(OS_WIN) && !defined(OS_APPLE) TEST_F(TCPBoundSocketTest, ListenError) { // Bind a socket. mojo::Remote<mojom::TCPBoundSocket> bound_socket1; @@ -285,7 +285,7 @@ TEST_F(TCPBoundSocketTest, ListenError) { EXPECT_TRUE(result == net::ERR_ADDRESS_IN_USE || result == net::ERR_INVALID_ARGUMENT); } -#endif // !defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_IOS) +#endif // !defined(OS_WIN) && !defined(OS_APPLE) // Test the case bind succeeds, and transfer some data. TEST_F(TCPBoundSocketTest, ReadWrite) { diff --git a/chromium/services/network/tcp_socket_unittest.cc b/chromium/services/network/tcp_socket_unittest.cc index b0be79ae427..9e65857c518 100644 --- a/chromium/services/network/tcp_socket_unittest.cc +++ b/chromium/services/network/tcp_socket_unittest.cc @@ -38,7 +38,7 @@ #include "services/network/tcp_server_socket.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MAC) #include "base/mac/mac_util.h" #endif @@ -575,7 +575,7 @@ TEST_F(TCPSocketTest, SocketClosed) { int result = observer()->WaitForWriteError(); bool result_ok = result == net::ERR_CONNECTION_RESET || result == net::ERR_CONNECTION_ABORTED; -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MAC) // On some macOS kernels, send() on a closing TCP socket can return // EPROTOTYPE, which is unknown to the net stack and gets mapped to // net::ERR_FAILED. diff --git a/chromium/services/network/throttling/throttling_network_interceptor.cc b/chromium/services/network/throttling/throttling_network_interceptor.cc index 203d6d37de0..4a83c2b57e3 100644 --- a/chromium/services/network/throttling/throttling_network_interceptor.cc +++ b/chromium/services/network/throttling/throttling_network_interceptor.cc @@ -18,16 +18,11 @@ namespace network { namespace { -int64_t kPacketSize = 1500; +constexpr int64_t kPacketSize = 1500; base::TimeDelta CalculateTickLength(double throughput) { - if (!throughput) - return base::TimeDelta::FromMicroseconds(1); - int64_t us_tick_length = (1000000L * kPacketSize) / throughput; - DCHECK(us_tick_length != 0); - if (us_tick_length == 0) - us_tick_length = 1; - return base::TimeDelta::FromMicroseconds(us_tick_length); + return throughput ? base::TimeDelta::FromSecondsD(kPacketSize / throughput) + : base::TimeDelta::FromMicroseconds(1); } } // namespace @@ -109,7 +104,7 @@ uint64_t ThrottlingNetworkInterceptor::UpdateThrottledRecords( return last_tick; } - int64_t new_tick = (now - offset_) / tick_length; + int64_t new_tick = (now - offset_).IntDiv(tick_length); int64_t ticks = new_tick - last_tick; int64_t length = records->size(); diff --git a/chromium/services/network/throttling/throttling_network_transaction.cc b/chromium/services/network/throttling/throttling_network_transaction.cc index 75b96109880..589ec4723e0 100644 --- a/chromium/services/network/throttling/throttling_network_transaction.cc +++ b/chromium/services/network/throttling/throttling_network_transaction.cc @@ -274,8 +274,8 @@ void ThrottlingNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper( } void ThrottlingNetworkTransaction::SetBeforeNetworkStartCallback( - const BeforeNetworkStartCallback& callback) { - network_transaction_->SetBeforeNetworkStartCallback(callback); + BeforeNetworkStartCallback callback) { + network_transaction_->SetBeforeNetworkStartCallback(std::move(callback)); } void ThrottlingNetworkTransaction::SetRequestHeadersCallback( @@ -288,6 +288,11 @@ void ThrottlingNetworkTransaction::SetResponseHeadersCallback( network_transaction_->SetResponseHeadersCallback(std::move(callback)); } +void ThrottlingNetworkTransaction::SetConnectedCallback( + const ConnectedCallback& callback) { + network_transaction_->SetConnectedCallback(callback); +} + int ThrottlingNetworkTransaction::ResumeNetworkStart() { if (CheckFailed()) return net::ERR_INTERNET_DISCONNECTED; diff --git a/chromium/services/network/throttling/throttling_network_transaction.h b/chromium/services/network/throttling/throttling_network_transaction.h index d5a4da0b40e..a9adb443fc5 100644 --- a/chromium/services/network/throttling/throttling_network_transaction.h +++ b/chromium/services/network/throttling/throttling_network_transaction.h @@ -78,7 +78,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ThrottlingNetworkTransaction void SetWebSocketHandshakeStreamCreateHelper( net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) override; void SetBeforeNetworkStartCallback( - const BeforeNetworkStartCallback& callback) override; + BeforeNetworkStartCallback callback) override; + void SetConnectedCallback(const ConnectedCallback& callback) override; void SetRequestHeadersCallback(net::RequestHeadersCallback callback) override; void SetResponseHeadersCallback( net::ResponseHeadersCallback callback) override; diff --git a/chromium/services/network/tls_socket_factory.cc b/chromium/services/network/tls_socket_factory.cc index 7f7dd14fc8b..dd9f19903ed 100644 --- a/chromium/services/network/tls_socket_factory.cc +++ b/chromium/services/network/tls_socket_factory.cc @@ -51,7 +51,8 @@ TLSSocketFactory::TLSSocketFactory( url_request_context->transport_security_state(), url_request_context->cert_transparency_verifier(), url_request_context->ct_policy_enforcer(), - nullptr /* Disables SSL session caching */), + nullptr /* Disables SSL session caching */, + url_request_context->sct_auditing_delegate()), client_socket_factory_(nullptr), ssl_config_service_(url_request_context->ssl_config_service()) { if (http_context) { @@ -132,7 +133,8 @@ void TLSSocketFactory::CreateTLSClientSocket( no_verification_transport_security_state_.get(), no_verification_cert_transparency_verifier_.get(), no_verification_ct_policy_enforcer_.get(), - nullptr /* no session cache */); + nullptr /* no session cache */, + nullptr /* disable sct auditing */); } ssl_client_context = no_verification_ssl_client_context_.get(); send_ssl_info = true; diff --git a/chromium/services/network/trust_tokens/BUILD.gn b/chromium/services/network/trust_tokens/BUILD.gn index 2e105004a31..93b85014834 100644 --- a/chromium/services/network/trust_tokens/BUILD.gn +++ b/chromium/services/network/trust_tokens/BUILD.gn @@ -162,6 +162,7 @@ source_set("tests") { ":trust_tokens", "//base", "//base/test:test_support", + "//base/util/ranges", "//components/cbor", "//components/sqlite_proto", "//net", diff --git a/chromium/services/network/trust_tokens/sqlite_trust_token_persister_unittest.cc b/chromium/services/network/trust_tokens/sqlite_trust_token_persister_unittest.cc index 908d96fd0eb..d89947bb068 100644 --- a/chromium/services/network/trust_tokens/sqlite_trust_token_persister_unittest.cc +++ b/chromium/services/network/trust_tokens/sqlite_trust_token_persister_unittest.cc @@ -85,7 +85,7 @@ TEST(SQLiteTrustTokenPersister, PutReinitializeAndGet) { // database asynchronously, so as not to leak after the test concludes. env.RunUntilIdle(); - base::DeleteFile(temp_path, false); + base::DeleteFile(temp_path); } // Ensure that it's possible to create a Trust Tokens persister on top of a diff --git a/chromium/services/network/trust_tokens/trust_token_key_commitments.cc b/chromium/services/network/trust_tokens/trust_token_key_commitments.cc index e762e32fb0b..4b50e745c68 100644 --- a/chromium/services/network/trust_tokens/trust_token_key_commitments.cc +++ b/chromium/services/network/trust_tokens/trust_token_key_commitments.cc @@ -36,10 +36,10 @@ ParseCommitmentsFromCommandLine() { raw_commitments)) { return std::move(*parsed); } else { - // Crash loudly here because the user presumably only provides key + // Complain loudly here because the user presumably only provides key // commitments through the command line out of a desire to _use_ the key // commitments. - LOG(FATAL) + LOG(ERROR) << "Couldn't parse Trust Tokens key commitments from the command line: " << raw_commitments; } diff --git a/chromium/services/network/trust_tokens/trust_token_request_helper_factory.cc b/chromium/services/network/trust_tokens/trust_token_request_helper_factory.cc index 130509bbee4..9ba6d9d3070 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_helper_factory.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_helper_factory.cc @@ -127,20 +127,29 @@ void TrustTokenRequestHelperFactory::ConstructHelperUsingStore( } case mojom::TrustTokenOperationType::kSigning: { - base::Optional<SuitableTrustTokenOrigin> maybe_issuer; - if (params->issuer) { - maybe_issuer = - SuitableTrustTokenOrigin::Create(std::move(*params->issuer)); - } - - if (!maybe_issuer) { - LogOutcome(net_log, "Missing/unsuitable 'issuer' parameter"); + if (params->issuers.empty()) { + LogOutcome(net_log, "Empty 'issuers' parameter"); std::move(done).Run(mojom::TrustTokenOperationStatus::kInvalidArgument); return; } + std::vector<SuitableTrustTokenOrigin> issuers; + for (url::Origin& potentially_unsuitable_issuer : params->issuers) { + base::Optional<SuitableTrustTokenOrigin> maybe_issuer = + SuitableTrustTokenOrigin::Create( + std::move(potentially_unsuitable_issuer)); + if (!maybe_issuer) { + LogOutcome(net_log, "Unsuitable issuer in 'issuers' parameter"); + std::move(done).Run( + mojom::TrustTokenOperationStatus::kInvalidArgument); + return; + } + + issuers.emplace_back(std::move(*maybe_issuer)); + } + TrustTokenRequestSigningHelper::Params signing_params( - std::move(*maybe_issuer), top_frame_origin, + std::move(issuers), top_frame_origin, std::move(params->additional_signed_headers), params->include_timestamp_header, params->sign_request_data, params->possibly_unsafe_additional_signing_data); diff --git a/chromium/services/network/trust_tokens/trust_token_request_helper_factory_unittest.cc b/chromium/services/network/trust_tokens/trust_token_request_helper_factory_unittest.cc index 10c2b2cbb41..8fa823a05b0 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_helper_factory_unittest.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_helper_factory_unittest.cc @@ -54,8 +54,8 @@ class TrustTokenRequestHelperFactoryTest : public ::testing::Test { TrustTokenRequestHelperFactoryTest() { suitable_request_ = CreateSuitableRequest(); suitable_params_ = mojom::TrustTokenParams::New(); - suitable_params_->issuer = - url::Origin::Create(GURL("https://issuer.example")); + suitable_params_->issuers.push_back( + url::Origin::Create(GURL("https://issuer.example"))); } protected: @@ -146,21 +146,22 @@ TEST_F(TrustTokenRequestHelperFactoryTest, ForbiddenHeaders) { } TEST_F(TrustTokenRequestHelperFactoryTest, - CreatingSigningHelperRequiresSuitableIssuer) { + CreatingSigningHelperRequiresSuitableIssuers) { auto request = CreateSuitableRequest(); auto params = suitable_params().Clone(); params->type = mojom::TrustTokenOperationType::kSigning; - params->issuer.reset(); + params->issuers.clear(); EXPECT_EQ(CreateHelperAndWaitForResult(*request, *params).status(), mojom::TrustTokenOperationStatus::kInvalidArgument); - params->issuer = UnsuitableUntrustworthyOrigin(); + params->issuers.push_back(UnsuitableUntrustworthyOrigin()); EXPECT_EQ(CreateHelperAndWaitForResult(*request, *params).status(), mojom::TrustTokenOperationStatus::kInvalidArgument); - params->issuer = UnsuitableNonHttpNonHttpsOrigin(); + params->issuers.clear(); + params->issuers.push_back(UnsuitableNonHttpNonHttpsOrigin()); EXPECT_EQ(CreateHelperAndWaitForResult(*request, *params).status(), mojom::TrustTokenOperationStatus::kInvalidArgument); } diff --git a/chromium/services/network/trust_tokens/trust_token_request_issuance_helper.cc b/chromium/services/network/trust_tokens/trust_token_request_issuance_helper.cc index 49ac06d0aea..d4dcbaaa7b3 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_issuance_helper.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_issuance_helper.cc @@ -104,9 +104,6 @@ void TrustTokenRequestIssuanceHelper::Begin( net::URLRequest* request, base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done) { DCHECK(request); - DCHECK(!request->initiator() || - IsOriginPotentiallyTrustworthy(*request->initiator())) - << *request->initiator(); net_log_.BeginEvent( net::NetLogEventType::TRUST_TOKEN_OPERATION_BEGIN_ISSUANCE); diff --git a/chromium/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc b/chromium/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc index 7799e2af869..df04c1c6924 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc @@ -7,6 +7,7 @@ #include <memory> #include "base/callback.h" +#include "base/no_destructor.h" #include "base/test/task_environment.h" #include "net/base/load_flags.h" #include "net/base/request_priority.h" diff --git a/chromium/services/network/trust_tokens/trust_token_request_redemption_helper.cc b/chromium/services/network/trust_tokens/trust_token_request_redemption_helper.cc index 2578edaf0ee..db260dd5887 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_redemption_helper.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_redemption_helper.cc @@ -73,9 +73,6 @@ void TrustTokenRequestRedemptionHelper::Begin( net::URLRequest* request, base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done) { DCHECK(request); - DCHECK(!request->initiator() || - IsOriginPotentiallyTrustworthy(*request->initiator())) - << *request->initiator(); net_log_.BeginEvent( net::NetLogEventType::TRUST_TOKEN_OPERATION_BEGIN_REDEMPTION); diff --git a/chromium/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc b/chromium/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc index 87661babf5c..88888c91ea7 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc @@ -7,6 +7,7 @@ #include <memory> #include "base/callback.h" +#include "base/no_destructor.h" #include "base/test/task_environment.h" #include "net/base/load_flags.h" #include "net/base/request_priority.h" diff --git a/chromium/services/network/trust_tokens/trust_token_request_signing_helper.cc b/chromium/services/network/trust_tokens/trust_token_request_signing_helper.cc index 5a185ef4261..f5d2d7201d7 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_signing_helper.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_signing_helper.cc @@ -44,6 +44,7 @@ void LogOutcome(const net::NetLogWithSource& log, base::StringPiece outcome) { } } // namespace + namespace internal { // Parse the Signed-Headers input header as a Structured Headers Draft 15 list @@ -91,12 +92,14 @@ namespace { using Params = TrustTokenRequestSigningHelper::Params; -// Constants for keys and values in the Sec-Signature header: +// Constants for keys and values in the generated headers: const char kSignatureHeaderSignRequestDataIncludeValue[] = "include"; const char kSignatureHeaderSignRequestDataHeadersOnlyValue[] = "headers-only"; +const char kSignatureHeaderSignaturesKey[] = "signatures"; const char kSignatureHeaderSignRequestDataKey[] = "sign-request-data"; const char kSignatureHeaderPublicKeyKey[] = "public-key"; const char kSignatureHeaderSignatureKey[] = "sig"; +const char kRedemptionRecordHeaderRedemptionRecordKey[] = "redemption-record"; std::vector<std::string> Lowercase(std::vector<std::string> in) { for (std::string& str : in) { @@ -181,12 +184,37 @@ GetHeadersToSignAndUpdateSignedHeadersHeader( } void AttachSignedRedemptionRecordHeader(net::URLRequest* request, - const std::string& value) { + std::string value) { request->SetExtraRequestHeaderByName( kTrustTokensRequestHeaderSecSignedRedemptionRecord, value, /*overwrite=*/true); } +// Builds a Trust Tokens signed redemption record header, which is logically an +// issuer-to-SRR map but implemented as a Structured Headers Draft 15 +// parameterized list (essentially a list where each member has an associated +// dictionary). +base::Optional<std::string> ConstructSignedRedemptionRecordHeader( + const base::flat_map<SuitableTrustTokenOrigin, + SignedTrustTokenRedemptionRecord>& + records_per_issuer) { + net::structured_headers::List header_items; + + for (const auto& issuer_and_record : records_per_issuer) { + net::structured_headers::Item issuer_item( + issuer_and_record.first.Serialize(), + net::structured_headers::Item::ItemType::kStringType); + net::structured_headers::Item redemption_record_item( + issuer_and_record.second.body(), + net::structured_headers::Item::ItemType::kByteSequenceType); + header_items.emplace_back(net::structured_headers::ParameterizedMember( + std::move(issuer_item), {{kRedemptionRecordHeaderRedemptionRecordKey, + std::move(redemption_record_item)}})); + } + + return net::structured_headers::SerializeList(std::move(header_items)); +} + } // namespace TrustTokenRequestSigningHelper::TrustTokenRequestSigningHelper( @@ -204,13 +232,13 @@ TrustTokenRequestSigningHelper::TrustTokenRequestSigningHelper( TrustTokenRequestSigningHelper::~TrustTokenRequestSigningHelper() = default; Params::Params( - SuitableTrustTokenOrigin issuer, + std::vector<SuitableTrustTokenOrigin> issuers, SuitableTrustTokenOrigin toplevel, std::vector<std::string> additional_headers_to_sign, bool should_add_timestamp, mojom::TrustTokenSignRequestData sign_request_data, base::Optional<std::string> possibly_unsafe_additional_signing_data) - : issuer(std::move(issuer)), + : issuers(std::move(issuers)), toplevel(std::move(toplevel)), additional_headers_to_sign(std::move(additional_headers_to_sign)), should_add_timestamp(should_add_timestamp), @@ -220,7 +248,9 @@ Params::Params( Params::Params(SuitableTrustTokenOrigin issuer, SuitableTrustTokenOrigin toplevel) - : issuer(std::move(issuer)), toplevel(std::move(toplevel)) {} + : toplevel(std::move(toplevel)) { + issuers.emplace_back(std::move(issuer)); +} Params::~Params() = default; Params::Params(const Params&) = default; // The type alias causes a linter false positive. @@ -234,9 +264,6 @@ void TrustTokenRequestSigningHelper::Begin( net::URLRequest* request, base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done) { DCHECK(request); - DCHECK(!request->initiator() || - IsOriginPotentiallyTrustworthy(*request->initiator())) - << *request->initiator(); #if DCHECK_IS_ON() // Add some postcondition checking on return. done = base::BindOnce( @@ -270,22 +297,41 @@ void TrustTokenRequestSigningHelper::Begin( net_log_.BeginEvent( net::NetLogEventType::TRUST_TOKEN_OPERATION_BEGIN_SIGNING); - // The comments below are the steps in the "Redemption record attachment and - // request signing" pseudocode in https://bit.ly/trust-token-dd + // The numbered comments below are the steps in the "Redemption record + // attachment and request signing" pseudocode in https://bit.ly/trust-token-dd - base::Optional<SignedTrustTokenRedemptionRecord> maybe_redemption_record = - token_store_->RetrieveNonstaleRedemptionRecord(params_.issuer, - params_.toplevel); + // (Because of the chracteristics of the protocol, this map is expected to + // have at most ~5 elements.) + base::flat_map<SuitableTrustTokenOrigin, SignedTrustTokenRedemptionRecord> + records_per_issuer; - if (!maybe_redemption_record) { + // 1. For each issuer specified, search storage for a non-expired SRR + // corresponding to that issuer and the request’s initiating top-level origin. + for (const SuitableTrustTokenOrigin& issuer : params_.issuers) { + base::Optional<SignedTrustTokenRedemptionRecord> maybe_redemption_record = + token_store_->RetrieveNonstaleRedemptionRecord(issuer, + params_.toplevel); + if (!maybe_redemption_record) + continue; + + records_per_issuer[issuer] = std::move(*maybe_redemption_record); + } + + if (records_per_issuer.empty()) { AttachSignedRedemptionRecordHeader(request, std::string()); - LogOutcome(net_log_, "No SRR for this (issuer, top-level context) pair"); + LogOutcome(net_log_, + "No SRR for any of the given issuers, in the operation's " + "top-level context"); std::move(done).Run(mojom::TrustTokenOperationStatus::kOk); return; } + // 2.a. If the request’s additionalSigningData argument is nonempty, add a + // Sec-Trust-Tokens-Additional-Signing-Data header to the request with its + // value equal to that of the request’s additionalSigningData argument. if (params_.possibly_unsafe_additional_signing_data) { + // 2.a.i. If it is longer than 2048 bytes, raise an error. if (params_.possibly_unsafe_additional_signing_data->size() > kTrustTokenAdditionalSigningDataMaxSizeBytes) { LogOutcome(net_log_, "Overly long additionalSigningData"); @@ -295,6 +341,8 @@ void TrustTokenRequestSigningHelper::Begin( return; } + // 2.a.ii. If it is not a valid HTTP header value (contains \r, \n, or \0), + // raise an error. if (!net::HttpUtil::IsValidHeaderValue( *params_.possibly_unsafe_additional_signing_data)) { LogOutcome(net_log_, @@ -313,6 +361,9 @@ void TrustTokenRequestSigningHelper::Begin( DCHECK(!request->extra_request_headers().HasHeader( kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData)); + // 2.a.iii. Add a Sec-Trust-Tokens-Additional-Signing-Data header to the + // request with its value equal to that of the request’s + // additionalSigningData argument. request->SetExtraRequestHeaderByName( kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData, *params_.possibly_unsafe_additional_signing_data, @@ -322,6 +373,11 @@ void TrustTokenRequestSigningHelper::Begin( kTrustTokensRequestHeaderSecTrustTokensAdditionalSigningData); } + // 2.b. Merge the additionalRequestHeaders Fetch parameter’s contents into the + // request’s Signed-Headers header (creating a header if previously absent). + // If the request has a Sec-Trust-Tokens-Additional-Signing-Data header, + // append “Sec-Trust-Tokens-Additional-Signing-Data” to the request’s + // Signed-Headers header. base::Optional<std::vector<std::string>> maybe_headers_to_sign = GetHeadersToSignAndUpdateSignedHeadersHeader( request, params_.additional_headers_to_sign); @@ -336,8 +392,24 @@ void TrustTokenRequestSigningHelper::Begin( return; } - AttachSignedRedemptionRecordHeader(request, maybe_redemption_record->body()); + // 2.c. Attach the SRRs in a Sec-Signed-Redemption-Record header. + if (base::Optional<std::string> maybe_signed_redemption_record_header = + ConstructSignedRedemptionRecordHeader(records_per_issuer)) { + AttachSignedRedemptionRecordHeader( + request, std::move(*maybe_signed_redemption_record_header)); + } else { + AttachSignedRedemptionRecordHeader(request, std::string()); + LogOutcome( + net_log_, + "Unexpected internal error serializing Sec-Signed-Redemption-Record" + " header."); + std::move(done).Run(mojom::TrustTokenOperationStatus::kOk); + return; + } + + // 2.d. If specified by the request’s includeTimestampHeader parameter, add a + // Sec-Time header containing a high-resolution timestamp, encoded in ISO8601. if (params_.should_add_timestamp) { request->SetExtraRequestHeaderByName(kTrustTokensRequestHeaderSecTime, base::TimeToISO8601(base::Time::Now()), @@ -350,39 +422,58 @@ void TrustTokenRequestSigningHelper::Begin( return; } - base::Optional<std::vector<uint8_t>> maybe_signature = - GetSignature(request, *maybe_redemption_record, *maybe_headers_to_sign); - - if (!maybe_signature) { - AttachSignedRedemptionRecordHeader(request, std::string()); - request->RemoveRequestHeaderByName(kTrustTokensRequestHeaderSecTime); - request->RemoveRequestHeaderByName(kTrustTokensRequestHeaderSignedHeaders); - - LogOutcome(net_log_, "Internal error generating signature"); - std::move(done).Run(mojom::TrustTokenOperationStatus::kOk); - return; + // 2.e. If the request’s signRequestData Fetch parameter is not “omit”, follow + // the steps in the Computing an outgoing request’s signatures section to add + // a Sec-Signature header containing, for each issuer, a signature over some + // of the request’s data (e.g. a subset of its headers) generated using a key + // bound to a prior redemption against that issuer. + base::flat_map<SuitableTrustTokenOrigin, std::vector<uint8_t>> + signatures_per_issuer; + + for (const auto& issuer_and_record : records_per_issuer) { + if (base::Optional<std::vector<uint8_t>> maybe_signature = GetSignature( + request, issuer_and_record.second, *maybe_headers_to_sign)) { + signatures_per_issuer[issuer_and_record.first] = + std::move(*maybe_signature); + } else { + // Failure isn't likely and would mean that the signing key---which we + // generate ourselves, during redemption---somehow got corrupted, or there + // was some kind of internal error generating the signature in the + // underlying cryptography library. + net_log_.AddEntry( + net::NetLogEventType::TRUST_TOKEN_OPERATION_BEGIN_SIGNING, + net::NetLogEventPhase::NONE, + [&issuer_and_record](net::NetLogCaptureMode mode) { + base::Value ret(base::Value::Type::DICTIONARY); + ret.SetStringPath("failed_signing_params.issuer", + issuer_and_record.first.Serialize()); + if (net::NetLogCaptureIncludesSensitive(mode)) { + ret.SetStringPath("failed_signing_params.key", + issuer_and_record.second.signing_key()); + } + return ret; + }); + } } - base::Optional<std::string> maybe_signature_header = BuildSignatureHeader( - maybe_redemption_record->public_key(), - base::StringPiece(reinterpret_cast<const char*>(maybe_signature->data()), - maybe_signature->size())); - - // Error serializing the header. Not expected. - if (!maybe_signature_header) { + if (base::Optional<std::string> maybe_signature_header = + BuildSignatureHeaderIfAtLeastOneSignatureIsPresent( + records_per_issuer, signatures_per_issuer)) { + request->SetExtraRequestHeaderByName(kTrustTokensRequestHeaderSecSignature, + std::move(*maybe_signature_header), + /*overwrite=*/true); + } else { AttachSignedRedemptionRecordHeader(request, std::string()); request->RemoveRequestHeaderByName(kTrustTokensRequestHeaderSecTime); request->RemoveRequestHeaderByName(kTrustTokensRequestHeaderSignedHeaders); - LogOutcome(net_log_, "Internal error serializing signature header"); + LogOutcome(net_log_, + "Internal error serializing signature header, or generating all " + "issuers' signatures failed."); std::move(done).Run(mojom::TrustTokenOperationStatus::kOk); return; } - request->SetExtraRequestHeaderByName(kTrustTokensRequestHeaderSecSignature, - *maybe_signature_header, - /*overwrite=*/true); - LogOutcome(net_log_, "Success"); std::move(done).Run(mojom::TrustTokenOperationStatus::kOk); } @@ -393,33 +484,79 @@ void TrustTokenRequestSigningHelper::Finalize( return std::move(done).Run(mojom::TrustTokenOperationStatus::kOk); } -base::Optional<std::string> -TrustTokenRequestSigningHelper::BuildSignatureHeader( - base::StringPiece public_key, - base::StringPiece signature) { +namespace { + +// Given a redemption record and a signature bytestring, returns a { +// "public-key": <public key>, +// "sig": <signature> +// } +// nested map, corresponding to a single entry in the Sec-Signature header's +// top-level list. +net::structured_headers::Parameters ConstructKeyAndSignaturePair( + const SignedTrustTokenRedemptionRecord& redemption_record, + base::span<const uint8_t> signature_bytes) { + net::structured_headers::Item public_key( + redemption_record.public_key(), + net::structured_headers::Item::ItemType::kByteSequenceType); + net::structured_headers::Item signature(net::structured_headers::Item( + std::string(reinterpret_cast<const char*>(signature_bytes.data()), + signature_bytes.size()), + net::structured_headers::Item::ItemType::kByteSequenceType)); + + return {{kSignatureHeaderPublicKeyKey, std::move(public_key)}, + {kSignatureHeaderSignatureKey, std::move(signature)}}; +} + +} // namespace + +base::Optional<std::string> TrustTokenRequestSigningHelper:: + BuildSignatureHeaderIfAtLeastOneSignatureIsPresent( + const base::flat_map<SuitableTrustTokenOrigin, + SignedTrustTokenRedemptionRecord>& + records_per_issuer, + const base::flat_map<SuitableTrustTokenOrigin, std::vector<uint8_t>>& + signatures_per_issuer) { + if (signatures_per_issuer.empty()) + return base::nullopt; + net::structured_headers::Dictionary header_items; - header_items[kSignatureHeaderPublicKeyKey] = - net::structured_headers::ParameterizedMember( - net::structured_headers::Item( - std::string(public_key), - net::structured_headers::Item::ItemType::kByteSequenceType), - {}); - header_items[kSignatureHeaderSignatureKey] = + std::vector<net::structured_headers::ParameterizedItem> keys_and_signatures; + for (const auto& kv : signatures_per_issuer) { + const SuitableTrustTokenOrigin& issuer = kv.first; + const std::vector<uint8_t>& signature = kv.second; + + keys_and_signatures.emplace_back(net::structured_headers::ParameterizedItem( + net::structured_headers::Item( + issuer.Serialize(), + net::structured_headers::Item::ItemType::kStringType), + // records_per_issuer is guaranteed to have all of the keys that + // signatures_per_issuer does, so using |at| is safe: + ConstructKeyAndSignaturePair(records_per_issuer.at(issuer), + signature))); + } + + header_items[kSignatureHeaderSignaturesKey] = net::structured_headers::ParameterizedMember( - net::structured_headers::Item( - std::string(signature), - net::structured_headers::Item::ItemType::kByteSequenceType), - {}); + std::move(keys_and_signatures), + net::structured_headers::Parameters()); // A value of kOmit denotes not wanting the request signed at all, so it'd be // a caller error if we were trying to sign the request with it set. DCHECK_NE(params_.sign_request_data, mojom::TrustTokenSignRequestData::kOmit); - const char* sign_request_data_value = - params_.sign_request_data == mojom::TrustTokenSignRequestData::kInclude - ? kSignatureHeaderSignRequestDataIncludeValue - : kSignatureHeaderSignRequestDataHeadersOnlyValue; + const char* sign_request_data_value = ""; + switch (params_.sign_request_data) { + case mojom::TrustTokenSignRequestData::kInclude: + sign_request_data_value = kSignatureHeaderSignRequestDataIncludeValue; + break; + case mojom::TrustTokenSignRequestData::kHeadersOnly: + sign_request_data_value = kSignatureHeaderSignRequestDataHeadersOnlyValue; + break; + case mojom::TrustTokenSignRequestData::kOmit: + NOTREACHED(); // "omit" handled (far) above. + break; + } header_items[kSignatureHeaderSignRequestDataKey] = net::structured_headers::ParameterizedMember( diff --git a/chromium/services/network/trust_tokens/trust_token_request_signing_helper.h b/chromium/services/network/trust_tokens/trust_token_request_signing_helper.h index 831ee3a68b4..16831045bc0 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_signing_helper.h +++ b/chromium/services/network/trust_tokens/trust_token_request_signing_helper.h @@ -73,7 +73,7 @@ class TrustTokenRequestSigningHelper : public TrustTokenRequestHelper { struct Params { // Refer to fields' comments for their semantics. - Params(SuitableTrustTokenOrigin issuer, + Params(std::vector<SuitableTrustTokenOrigin> issuers, SuitableTrustTokenOrigin toplevel, std::vector<std::string> additional_headers_to_sign, bool should_add_timestamp, @@ -91,14 +91,14 @@ class TrustTokenRequestSigningHelper : public TrustTokenRequestHelper { Params(Params&&); Params& operator=(Params&&); - // |issuer| is the Trust Tokens issuer origin for which to retrieve a Signed - // Redemption Record and matching signing key. This must be both (1) HTTP or - // HTTPS and (2) "potentially trustworthy". This precondition is slightly - // involved because there are two needs: + // |issuers| contains the Trust Tokens issuer origins for which to retrieve + // Signed Redemption Records and matching signing keys. These must be both + // (1) HTTP or HTTPS and (2) "potentially trustworthy". This precondition is + // slightly involved because there are two needs: // 1. HTTP or HTTPS so that the scheme serializes in a sensible manner in - // order to serve as a key for persisting state. - // 2. potentially trustworthy origin to satisfy Web security requirements. - SuitableTrustTokenOrigin issuer; + // order to serve as a key for persisting state, + // 2. potentially trustworthy to satisfy Web security requirements. + std::vector<SuitableTrustTokenOrigin> issuers; // |toplevel| is the top-level origin of the initiating request. This must // satisfy the same preconditions as |issuer|. @@ -173,13 +173,13 @@ class TrustTokenRequestSigningHelper : public TrustTokenRequestHelper { TrustTokenRequestSigningHelper& operator=( const TrustTokenRequestSigningHelper&) = delete; - // Attempts to attach a Signed Redemption Record (SRR) corresponding - // to |request|'s initiating top-level origin and the provided issuer origin. + // Attempts to attach Signed Redemption Records (SRRs) corresponding + // to |request|'s initiating top-level origin and the provided issuer origins. // // ATTACHING THE REDEMPTION RECORD: - // In the case that an SRR is found and the requested headers to sign are - // well-formed, attaches a Sec-Signed-Redemption-Record header - // bearing the SRR and: + // In the case that an SRR is found for at least one provided issuer and the + // requested headers to sign are well-formed, attaches a + // Sec-Signed-Redemption-Record header bearing the SRRs and: // 1. if the request is configured for adding a Trust Tokens timestamp, // adds a timestamp header; // 2. if the request is configured for signing, computes the request's @@ -190,11 +190,12 @@ class TrustTokenRequestSigningHelper : public TrustTokenRequestHelper { // 1. The caller specified headers for signing other than those in // kSignableRequestHeaders (or if the request has a malformed or otherwise // invalid signed issuers list in its Signed-Headers header); or - // 2. |token_store_| contains no SRR for this issuer-toplevel pair; or + // 2. none of the provided issuers has an SRR corresponding to this top-level + // origin in |token_store_|; or // 3. an internal error occurs during signing or header serialization. // // POSTCONDITIONS: - // - Always returns kOk. This is to avoid aborting a request entirely to a + // - Always returns kOk. This is to avoid aborting a request entirely due to a // failure during signing; see the Trust Tokens design doc for more // discussion. // - On failure, the request will contain an empty @@ -211,16 +212,28 @@ class TrustTokenRequestSigningHelper : public TrustTokenRequestHelper { base::OnceCallback<void(mojom::TrustTokenOperationStatus)> done) override; private: - // Given (unencoded) bytestrings |public_key| and |signature|, returns the + // Given issuer-to-redemption-record and issuer-to-signature maps, returns a // Trust Tokens signature header, a serialized Structured Headers Draft 15 - // dictionary looking roughly like (order not guaranteed): - // public-key=:<base64(pk)>:, - // sig=:<base64(signature)>:, - // sign-request-data=include | headers-only + // dictionary with logical structure roughly + // "signatures": [ + // (<issuer 1>, { "public-key": <public key>, "sig": <signature> }), + // ….. + // (<issuer N>, { "public-key": <public key>, "sig": <signature> }) + // ], + // "sign-request-data": include | headers-only // - // Returns nullopt on serialization error. - base::Optional<std::string> BuildSignatureHeader(base::StringPiece public_key, - base::StringPiece signature); + // Returns nullopt on serialization error, or if |signatures_per_issuer| is + // empty. + // + // REQUIRES: Every issuer in |signatures_per_issuer| must have a corresponding + // signed redemption record in |records_per_issuer|. + base::Optional<std::string> + BuildSignatureHeaderIfAtLeastOneSignatureIsPresent( + const base::flat_map<SuitableTrustTokenOrigin, + SignedTrustTokenRedemptionRecord>& + records_per_issuer, + const base::flat_map<SuitableTrustTokenOrigin, std::vector<uint8_t>>& + signatures_per_issuer); // Returns a signature over |request|'s pertinent data (public key, // user-specified headers and, possibly, destination URL), or nullopt in case diff --git a/chromium/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc b/chromium/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc index 08a9bad1ae9..0270abe485d 100644 --- a/chromium/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc +++ b/chromium/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc @@ -18,11 +18,14 @@ #include "base/test/bind_test_util.h" #include "base/test/task_environment.h" #include "base/time/time_to_iso8601.h" +#include "base/util/ranges/algorithm.h" #include "components/cbor/reader.h" #include "components/cbor/values.h" #include "components/cbor/writer.h" #include "net/base/request_priority.h" #include "net/http/structured_headers.h" +#include "net/log/test_net_log.h" +#include "net/log/test_net_log_util.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_test_util.h" @@ -38,7 +41,6 @@ #include "url/gurl.h" #include "url/origin.h" -using ::testing::AnyOf; using ::testing::IsEmpty; using ::testing::Matches; using ::testing::Not; @@ -102,30 +104,35 @@ class FailingSigner : public TrustTokenRequestSigningHelper::Signer { } }; -// Reconstructs |request|'s canonical request data, extracts the signature from +// Reconstructs |request|'s canonical request data, extracts the signatures from // |request|'s Sec-Signature header, and uses the verification algorithm // provided by the template parameter |Signer| to check that the Sec-Signature -// header's contained signature verifies. +// header's contained signatures verify. template <typename Signer> -void ReconstructSigningDataAndAssertSignatureVerifies( - net::URLRequest* request) { +void ReconstructSigningDataAndAssertSignaturesVerify( + net::URLRequest* request, + size_t num_expected_signatures) { std::string error; - bool success = test::ReconstructSigningDataAndVerifySignature( + + std::map<std::string, std::string> verification_keys_per_issuer; + bool success = test::ReconstructSigningDataAndVerifySignatures( request->url(), request->extra_request_headers(), - base::BindOnce([](base::span<const uint8_t> data, - base::span<const uint8_t> signature, - base::span<const uint8_t> verification_key) { + base::BindRepeating([](base::span<const uint8_t> data, + base::span<const uint8_t> signature, + base::span<const uint8_t> verification_key) { return Signer().Verify(data, signature, verification_key); }), - &error); + &error, &verification_keys_per_issuer); ASSERT_TRUE(success) << error; + ASSERT_EQ(verification_keys_per_issuer.size(), num_expected_signatures); } -// Verifies that |request| has a Sec-Signature header with a "sig" field and -// extracts the request's signature from this field. -void AssertHasSignatureAndExtract(const net::URLRequest& request, - std::string* signature_out) { +// Verifies that |request| has a Sec-Signature header containing signatures and +// extracts the signature for each issuer to |signatures_out|. +void AssertHasSignaturesAndExtract( + const net::URLRequest& request, + std::map<std::string, std::string>* signatures_out) { std::string signature_header; ASSERT_TRUE(request.extra_request_headers().GetHeader("Sec-Signature", &signature_header)); @@ -133,12 +140,22 @@ void AssertHasSignatureAndExtract(const net::URLRequest& request, base::Optional<net::structured_headers::Dictionary> maybe_dictionary = net::structured_headers::ParseDictionary(signature_header); ASSERT_TRUE(maybe_dictionary); - ASSERT_TRUE(maybe_dictionary->contains("sig")); + ASSERT_TRUE(maybe_dictionary->contains("signatures")); + + for (auto& issuer_and_params : maybe_dictionary->at("signatures").member) { + net::structured_headers::Item& issuer_item = issuer_and_params.item; + ASSERT_TRUE(issuer_item.is_string()); + + auto signature_iterator = std::find_if( + issuer_and_params.params.begin(), issuer_and_params.params.end(), + [](auto& param) { return param.first == "sig"; }); - net::structured_headers::Item& sig_item = - maybe_dictionary->at("sig").member.front().item; - ASSERT_TRUE(sig_item.is_byte_sequence()); - *signature_out = sig_item.GetString(); + ASSERT_TRUE(signature_iterator != issuer_and_params.params.end()) + << "Missing signature"; + ASSERT_TRUE(signature_iterator->second.is_byte_sequence()); + signatures_out->emplace(issuer_item.GetString(), + signature_iterator->second.GetString()); + } } // Assert that the given signing data is a concatenation of the domain separator @@ -149,7 +166,7 @@ void AssertDecodesToCborAndExtractField(base::StringPiece signing_data, base::StringPiece field_name, std::string* field_value_out) { base::Optional<cbor::Value> parsed = cbor::Reader::Read(base::as_bytes( - // Skip over the "Trust Token v0" domain separator. + // Skip over the domain separator (e.g. "Trust Token v0"). base::make_span(signing_data) .subspan(base::size(TrustTokenRequestSigningHelper:: kRequestSigningDomainSeparator)))); @@ -178,6 +195,13 @@ MATCHER_P2(Header, return Matches(other_matcher)(header); } +SuitableTrustTokenOrigin CreateSuitableOriginOrDie(base::StringPiece spec) { + base::Optional<SuitableTrustTokenOrigin> maybe_origin = + SuitableTrustTokenOrigin::Create(GURL(spec)); + CHECK(maybe_origin) << "Failed to create a SuitableTrustTokenOrigin!"; + return *maybe_origin; +} + } // namespace TEST_F(TrustTokenRequestSigningHelperTest, WontSignIfNoRedemptionRecord) { @@ -199,8 +223,9 @@ TEST_F(TrustTokenRequestSigningHelperTest, WontSignIfNoRedemptionRecord) { mojom::TrustTokenOperationStatus result = ExecuteBeginOperationAndWaitForResult(&helper, my_request.get()); - // In failure cases, the signing helper should return kOk but attach an empty - // SRR header. + // In failure cases---in particular, in this case where none of the provided + // issuers has a signed redemption record in storage---the signing helper + // should return kOk but attach an empty SRR header. EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk); EXPECT_THAT(*my_request, Header("Sec-Signed-Redemption-Record", IsEmpty())); EXPECT_THAT(*my_request, Not(Header("Sec-Signature"))); @@ -222,7 +247,8 @@ TEST_F(TrustTokenRequestSigningHelperTest, MergesHeaders) { SignedTrustTokenRedemptionRecord my_record; my_record.set_public_key("key"); my_record.set_body("SRR body"); - store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, + my_record); TrustTokenRequestSigningHelper helper( store.get(), std::move(params), std::make_unique<FakeSigner>(), @@ -266,7 +292,8 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignedTrustTokenRedemptionRecord my_record; my_record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, + my_record); TrustTokenRequestSigningHelper helper( store.get(), std::move(params), std::make_unique<FakeSigner>(), @@ -311,7 +338,8 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignedTrustTokenRedemptionRecord my_record; my_record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, + my_record); TrustTokenRequestSigningHelper helper( store.get(), std::move(params), std::make_unique<FakeSigner>(), @@ -349,7 +377,8 @@ TEST_F(TrustTokenRequestSigningHelperTestWithMockTime, ProvidesTimeHeader) { SignedTrustTokenRedemptionRecord my_record; my_record.set_public_key("key"); my_record.set_body("look at me, I'm an SRR body"); - store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, + my_record); TrustTokenRequestSigningHelper helper( store.get(), std::move(params), std::make_unique<FakeSigner>(), @@ -367,6 +396,9 @@ TEST_F(TrustTokenRequestSigningHelperTestWithMockTime, ProvidesTimeHeader) { } // Test SRR attachment without request signing: +// - The two issuers with stored redemption records should appear in the header. +// - A third issuer without a corresponding redemption record in storage +// shouldn't appear in the header. TEST_F(TrustTokenRequestSigningHelperTest, RedemptionRecordAttachmentWithoutSigning) { std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting(); @@ -376,11 +408,26 @@ TEST_F(TrustTokenRequestSigningHelperTest, *SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com"))); params.should_add_timestamp = true; params.sign_request_data = mojom::TrustTokenSignRequestData::kOmit; - - SignedTrustTokenRedemptionRecord my_record; - my_record.set_body("look at me! I'm a signed redemption record"); - my_record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + params.issuers.push_back( + *SuitableTrustTokenOrigin::Create(GURL("https://second-issuer.example"))); + + SignedTrustTokenRedemptionRecord first_issuer_record; + first_issuer_record.set_body("look at me! I'm a signed redemption record"); + first_issuer_record.set_public_key("key"); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, + first_issuer_record); + + SignedTrustTokenRedemptionRecord second_issuer_record; + second_issuer_record.set_body( + "I'm another signed redemption record, distinct from the first"); + second_issuer_record.set_public_key("some other key"); + store->SetRedemptionRecord(params.issuers.back(), params.toplevel, + second_issuer_record); + + // Attempting to sign with an issuer with no redemption record in storage + // should be fine, resulting in the issuer getting ignored. + params.issuers.push_back( + *SuitableTrustTokenOrigin::Create(GURL("https://third-issuer.example"))); TrustTokenRequestSigningHelper helper( store.get(), std::move(params), std::make_unique<IdentitySigner>(), @@ -392,8 +439,23 @@ TEST_F(TrustTokenRequestSigningHelperTest, ExecuteBeginOperationAndWaitForResult(&helper, my_request.get()); ASSERT_EQ(result, mojom::TrustTokenOperationStatus::kOk); - EXPECT_THAT(*my_request, - Header("Sec-Signed-Redemption-Record", StrEq(my_record.body()))); + + std::string redemption_record_header; + ASSERT_TRUE(my_request->extra_request_headers().GetHeader( + "Sec-Signed-Redemption-Record", &redemption_record_header)); + std::map<SuitableTrustTokenOrigin, std::string> redemption_records_per_issuer; + std::string error; + ASSERT_TRUE(test::ExtractRedemptionRecordsFromHeader( + redemption_record_header, &redemption_records_per_issuer, &error)) + << error; + + EXPECT_THAT( + redemption_records_per_issuer, + UnorderedElementsAre( + Pair(CreateSuitableOriginOrDie("https://issuer.com"), + StrEq(first_issuer_record.body())), + Pair(CreateSuitableOriginOrDie("https://second-issuer.example"), + StrEq(second_issuer_record.body())))); EXPECT_THAT(*my_request, Header("Sec-Time")); EXPECT_THAT(*my_request, Not(Header("Sec-Signature"))); } @@ -410,11 +472,12 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyMinimal) { SignedTrustTokenRedemptionRecord my_record; my_record.set_public_key("key"); my_record.set_body("look at me, I'm an SRR body"); - store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, + my_record); // Giving an IdentitySigner to |helper| will mean that |helper| should provide // its entire signing data in the request's Sec-Signature header's "sig" - // field. ReconstructSigningDataAndAssertSignatureVerifies then reproduces + // field. ReconstructSigningDataAndAssertSignaturesVerify then reproduces // this canonical data's construction and checks that the reconstructed data // matches what |helper| produced. auto canonicalizer = std::make_unique<TrustTokenRequestCanonicalizer>(); @@ -430,11 +493,11 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyMinimal) { EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk); ASSERT_NO_FATAL_FAILURE( - ReconstructSigningDataAndAssertSignatureVerifies<IdentitySigner>( - my_request.get())); + ReconstructSigningDataAndAssertSignaturesVerify<IdentitySigner>( + my_request.get(), /*num_expected_signatures=*/1)); } -// Test a round-trip sign-and-verify with signed headers. +// Test a round-trip sign-and-verify with signed headers and multiple issuers. TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyWithHeaders) { std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting(); @@ -445,10 +508,18 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyWithHeaders) { SignedTrustTokenRedemptionRecord record; record.set_body("I am a signed token redemption record"); record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, record); params.additional_headers_to_sign = std::vector<std::string>{"Sec-Signed-Redemption-Record"}; + params.issuers.push_back( + *SuitableTrustTokenOrigin::Create(GURL("https://second-issuer.example"))); + SignedTrustTokenRedemptionRecord other_record; + other_record.set_body("I am a different signed token redemption record"); + other_record.set_public_key("some other key"); + store->SetRedemptionRecord(params.issuers.back(), params.toplevel, + other_record); + auto canonicalizer = std::make_unique<TrustTokenRequestCanonicalizer>(); TrustTokenRequestSigningHelper helper(store.get(), std::move(params), std::make_unique<IdentitySigner>(), @@ -461,8 +532,8 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyWithHeaders) { EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk); ASSERT_NO_FATAL_FAILURE( - ReconstructSigningDataAndAssertSignatureVerifies<IdentitySigner>( - my_request.get())); + ReconstructSigningDataAndAssertSignaturesVerify<IdentitySigner>( + my_request.get(), /*num_expected_signatures=*/2)); } // Test a round-trip sign-and-verify with signed headers when adding a timestamp @@ -480,7 +551,7 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyTimestampHeader) { SignedTrustTokenRedemptionRecord record; record.set_body("I am a signed token redemption record"); record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, record); auto canonicalizer = std::make_unique<TrustTokenRequestCanonicalizer>(); TrustTokenRequestSigningHelper helper(store.get(), std::move(params), @@ -495,15 +566,17 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyTimestampHeader) { EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk); ASSERT_NO_FATAL_FAILURE( - ReconstructSigningDataAndAssertSignatureVerifies<IdentitySigner>( - my_request.get())); + ReconstructSigningDataAndAssertSignaturesVerify<IdentitySigner>( + my_request.get(), /*num_expected_signatures=*/1)); - std::string signature_string; + // Because we're using an IdentitySigner, each signature will have value + // equal to the base64-encoded request signing data. + std::map<std::string, std::string> signatures; ASSERT_NO_FATAL_FAILURE( - AssertHasSignatureAndExtract(*my_request, &signature_string)); + AssertHasSignaturesAndExtract(*my_request, &signatures)); std::string retrieved_timestamp; ASSERT_NO_FATAL_FAILURE(AssertDecodesToCborAndExtractField( - signature_string, "sec-time", &retrieved_timestamp)); + signatures.begin()->second, "sec-time", &retrieved_timestamp)); } // Test a round-trip sign-and-verify additionally signing over the destination @@ -520,7 +593,7 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignedTrustTokenRedemptionRecord record; record.set_body("I am a signed token redemption record"); record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, record); params.additional_headers_to_sign = std::vector<std::string>{"Sec-Signed-Redemption-Record"}; @@ -536,23 +609,23 @@ TEST_F(TrustTokenRequestSigningHelperTest, ExecuteBeginOperationAndWaitForResult(&helper, my_request.get()); // In addition to testing that the signing data equals - // ReconstructSigningDataAndAssertSignatureVerifies's reconstruction of the + // ReconstructSigningDataAndAssertSignaturesVerify's reconstruction of the // data, explicitly check that it contains a "destination" field with the // right value. EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk); ASSERT_NO_FATAL_FAILURE( - ReconstructSigningDataAndAssertSignatureVerifies<IdentitySigner>( - my_request.get())); + ReconstructSigningDataAndAssertSignaturesVerify<IdentitySigner>( + my_request.get(), /*num_expected_signatures=*/1)); - // Because we're using an IdentitySigner, |signature_string| will have value + // Because we're using an IdentitySigner, each signature will have value // equal to the base64-encoded request signing data. - std::string signature_string; + std::map<std::string, std::string> signatures; ASSERT_NO_FATAL_FAILURE( - AssertHasSignatureAndExtract(*my_request, &signature_string)); + AssertHasSignaturesAndExtract(*my_request, &signatures)); std::string retrieved_url; ASSERT_NO_FATAL_FAILURE(AssertDecodesToCborAndExtractField( - signature_string, "destination", &retrieved_url)); + signatures.begin()->second, "destination", &retrieved_url)); ASSERT_EQ(retrieved_url, "destination.com"); } @@ -569,7 +642,9 @@ TEST_F(TrustTokenRequestSigningHelperTest, CatchesSignatureFailure) { SignedTrustTokenRedemptionRecord my_record; my_record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + my_record.set_signing_key("signing key"); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, + my_record); params.should_add_timestamp = true; params.additional_headers_to_sign = @@ -577,9 +652,12 @@ TEST_F(TrustTokenRequestSigningHelperTest, CatchesSignatureFailure) { // FailingSigner will fail to sign the request, so we should see the operation // fail. + net::RecordingTestNetLog net_log; TrustTokenRequestSigningHelper helper( store.get(), std::move(params), std::make_unique<FailingSigner>(), - std::make_unique<TrustTokenRequestCanonicalizer>()); + std::make_unique<TrustTokenRequestCanonicalizer>(), + net::NetLogWithSource::Make(&net_log, + net::NetLogSourceType::URL_REQUEST)); auto my_request = MakeURLRequest("https://destination.com/"); my_request->set_initiator( @@ -592,6 +670,18 @@ TEST_F(TrustTokenRequestSigningHelperTest, CatchesSignatureFailure) { EXPECT_THAT(*my_request, Not(Header("Sec-Time"))); EXPECT_THAT(*my_request, Not(Header("Sec-Signature"))); EXPECT_THAT(*my_request, Header("Sec-Signed-Redemption-Record", IsEmpty())); + EXPECT_TRUE(util::ranges::any_of( + net_log.GetEntriesWithType( + net::NetLogEventType::TRUST_TOKEN_OPERATION_BEGIN_SIGNING), + [](const net::NetLogEntry& entry) { + base::Optional<std::string> key = net::GetOptionalStringValueFromParams( + entry, "failed_signing_params.key"); + base::Optional<std::string> issuer = + net::GetOptionalStringValueFromParams( + entry, "failed_signing_params.issuer"); + return key && *key == "signing key" && issuer && + *issuer == "https://issuer.com"; + })); } // Test a round-trip sign-and-verify with signed headers when adding additional @@ -609,7 +699,7 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyAdditionalSigningData) { SignedTrustTokenRedemptionRecord record; record.set_body("I am a signed token redemption record"); record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, record); auto canonicalizer = std::make_unique<TrustTokenRequestCanonicalizer>(); TrustTokenRequestSigningHelper helper(store.get(), std::move(params), @@ -624,15 +714,17 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignAndVerifyAdditionalSigningData) { EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk); ASSERT_NO_FATAL_FAILURE( - ReconstructSigningDataAndAssertSignatureVerifies<IdentitySigner>( - my_request.get())); + ReconstructSigningDataAndAssertSignaturesVerify<IdentitySigner>( + my_request.get(), /*num_expected_signatures=*/1)); - std::string signature_string; + // Because we're using an IdentitySigner, each signature will have value + // equal to the base64-encoded request signing data. + std::map<std::string, std::string> signatures; ASSERT_NO_FATAL_FAILURE( - AssertHasSignatureAndExtract(*my_request, &signature_string)); + AssertHasSignaturesAndExtract(*my_request, &signatures)); std::string retrieved_additional_signing_data; ASSERT_NO_FATAL_FAILURE(AssertDecodesToCborAndExtractField( - signature_string, "sec-trust-tokens-additional-signing-data", + signatures.begin()->second, "sec-trust-tokens-additional-signing-data", &retrieved_additional_signing_data)); EXPECT_EQ(retrieved_additional_signing_data, "some additional data to sign"); @@ -651,7 +743,8 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignedTrustTokenRedemptionRecord my_record; my_record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, + my_record); TrustTokenRequestSigningHelper helper( store.get(), std::move(params), std::make_unique<FakeSigner>(), @@ -685,7 +778,8 @@ TEST_F(TrustTokenRequestSigningHelperTest, SignedTrustTokenRedemptionRecord my_record; my_record.set_public_key("key"); - store->SetRedemptionRecord(params.issuer, params.toplevel, my_record); + store->SetRedemptionRecord(params.issuers.front(), params.toplevel, + my_record); TrustTokenRequestSigningHelper helper( store.get(), std::move(params), std::make_unique<FakeSigner>(), diff --git a/chromium/services/network/udp_socket_test_util.cc b/chromium/services/network/udp_socket_test_util.cc deleted file mode 100644 index b05ada98a90..00000000000 --- a/chromium/services/network/udp_socket_test_util.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/network/udp_socket_test_util.h" - -#include <utility> - -#include "base/run_loop.h" -#include "base/test/bind_test_util.h" -#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace network { - -namespace test { - -UDPSocketTestHelper::UDPSocketTestHelper(mojo::Remote<mojom::UDPSocket>* socket) - : socket_(socket) {} - -UDPSocketTestHelper::~UDPSocketTestHelper() {} - -int UDPSocketTestHelper::ConnectSync(const net::IPEndPoint& remote_addr, - mojom::UDPSocketOptionsPtr options, - net::IPEndPoint* local_addr_out) { - base::RunLoop run_loop; - int net_error = net::ERR_FAILED; - socket_->get()->Connect( - remote_addr, std::move(options), - base::BindLambdaForTesting( - [&](int result, const base::Optional<net::IPEndPoint>& local_addr) { - net_error = result; - if (local_addr) { - *local_addr_out = local_addr.value(); - } - run_loop.Quit(); - })); - run_loop.Run(); - return net_error; -} - -int UDPSocketTestHelper::BindSync(const net::IPEndPoint& local_addr, - mojom::UDPSocketOptionsPtr options, - net::IPEndPoint* local_addr_out) { - base::RunLoop run_loop; - int net_error = net::ERR_FAILED; - socket_->get()->Bind( - local_addr, std::move(options), - base::BindLambdaForTesting( - [&](int result, const base::Optional<net::IPEndPoint>& local_addr) { - net_error = result; - if (local_addr) { - *local_addr_out = local_addr.value(); - } - run_loop.Quit(); - })); - run_loop.Run(); - return net_error; -} - -int UDPSocketTestHelper::SendToSync(const net::IPEndPoint& remote_addr, - const std::vector<uint8_t>& input) { - base::RunLoop run_loop; - int net_error = net::ERR_FAILED; - socket_->get()->SendTo( - remote_addr, input, - net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS), - base::BindLambdaForTesting([&](int result) { - net_error = result; - run_loop.Quit(); - })); - run_loop.Run(); - return net_error; -} - -int UDPSocketTestHelper::SendSync(const std::vector<uint8_t>& input) { - base::RunLoop run_loop; - int net_error = net::ERR_FAILED; - socket_->get()->Send( - input, - net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS), - base::BindLambdaForTesting([&](int result) { - net_error = result; - run_loop.Quit(); - })); - run_loop.Run(); - return net_error; -} - -int UDPSocketTestHelper::SetBroadcastSync(bool broadcast) { - base::RunLoop run_loop; - int net_error = net::ERR_FAILED; - socket_->get()->SetBroadcast(broadcast, - base::BindLambdaForTesting([&](int result) { - net_error = result; - run_loop.Quit(); - })); - run_loop.Run(); - return net_error; -} - -int UDPSocketTestHelper::SetSendBufferSizeSync(int send_buffer_size) { - base::RunLoop run_loop; - int net_error = net::ERR_FAILED; - socket_->get()->SetSendBufferSize(send_buffer_size, - base::BindLambdaForTesting([&](int result) { - net_error = result; - run_loop.Quit(); - })); - run_loop.Run(); - return net_error; -} - -int UDPSocketTestHelper::SetReceiveBufferSizeSync(int receive_buffer_size) { - base::RunLoop run_loop; - int net_error = net::ERR_FAILED; - socket_->get()->SetReceiveBufferSize( - receive_buffer_size, base::BindLambdaForTesting([&](int result) { - net_error = result; - run_loop.Quit(); - })); - run_loop.Run(); - return net_error; -} - -int UDPSocketTestHelper::JoinGroupSync(const net::IPAddress& group_address) { - base::RunLoop run_loop; - int net_error = net::ERR_FAILED; - socket_->get()->JoinGroup(group_address, - base::BindLambdaForTesting([&](int result) { - net_error = result; - run_loop.Quit(); - })); - run_loop.Run(); - return net_error; -} - -int UDPSocketTestHelper::LeaveGroupSync(const net::IPAddress& group_address) { - base::RunLoop run_loop; - int net_error = net::ERR_FAILED; - socket_->get()->LeaveGroup(group_address, - base::BindLambdaForTesting([&](int result) { - net_error = result; - run_loop.Quit(); - })); - run_loop.Run(); - return net_error; -} - -UDPSocketListenerImpl::ReceivedResult::ReceivedResult( - int net_error_arg, - const base::Optional<net::IPEndPoint>& src_addr_arg, - base::Optional<std::vector<uint8_t>> data_arg) - : net_error(net_error_arg), - src_addr(src_addr_arg), - data(std::move(data_arg)) {} - -UDPSocketListenerImpl::ReceivedResult::ReceivedResult( - const ReceivedResult& other) = default; - -UDPSocketListenerImpl::ReceivedResult::~ReceivedResult() {} - -UDPSocketListenerImpl::UDPSocketListenerImpl() - : run_loop_(std::make_unique<base::RunLoop>()), - expected_receive_count_(0) {} - -UDPSocketListenerImpl::~UDPSocketListenerImpl() {} - -void UDPSocketListenerImpl::WaitForReceivedResults(size_t count) { - DCHECK_LE(results_.size(), count); - DCHECK_EQ(0u, expected_receive_count_); - - if (results_.size() == count) - return; - - expected_receive_count_ = count; - run_loop_->Run(); - run_loop_ = std::make_unique<base::RunLoop>(); -} - -void UDPSocketListenerImpl::OnReceived( - int32_t result, - const base::Optional<net::IPEndPoint>& src_addr, - base::Optional<base::span<const uint8_t>> data) { - // OnReceive() API contracts specifies that this method will not be called - // with a |result| that is > 0. - DCHECK_GE(0, result); - DCHECK(result < 0 || data); - - results_.emplace_back(result, src_addr, - data ? base::make_optional(std::vector<uint8_t>( - data.value().begin(), data.value().end())) - : base::nullopt); - if (results_.size() == expected_receive_count_) { - expected_receive_count_ = 0; - run_loop_->Quit(); - } -} - -} // namespace test - -} // namespace network diff --git a/chromium/services/network/udp_socket_test_util.h b/chromium/services/network/udp_socket_test_util.h deleted file mode 100644 index a6134096955..00000000000 --- a/chromium/services/network/udp_socket_test_util.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_NETWORK_UDP_SOCKET_TEST_UTIL_H_ -#define SERVICES_NETWORK_UDP_SOCKET_TEST_UTIL_H_ - -#include <stdint.h> - -#include <memory> -#include <vector> - -#include "base/containers/span.h" -#include "base/macros.h" -#include "base/optional.h" -#include "base/run_loop.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "net/base/ip_endpoint.h" -#include "net/base/net_errors.h" -#include "services/network/public/mojom/udp_socket.mojom.h" - -namespace network { - -namespace test { - -// Helper functions to invoke the corresponding mojo APIs and wait for -// completion. -class UDPSocketTestHelper { - public: - explicit UDPSocketTestHelper(mojo::Remote<mojom::UDPSocket>* socket); - ~UDPSocketTestHelper(); - int ConnectSync(const net::IPEndPoint& remote_addr, - mojom::UDPSocketOptionsPtr options, - net::IPEndPoint* local_addr_out); - int BindSync(const net::IPEndPoint& local_addr, - mojom::UDPSocketOptionsPtr options, - net::IPEndPoint* local_addr_out); - int SendToSync(const net::IPEndPoint& remote_addr, - const std::vector<uint8_t>& input); - int SendSync(const std::vector<uint8_t>& input); - int SetBroadcastSync(bool broadcast); - int SetSendBufferSizeSync(int send_buffer_size); - int SetReceiveBufferSizeSync(int receive_buffer_size); - int JoinGroupSync(const net::IPAddress& group_address); - int LeaveGroupSync(const net::IPAddress& group_address); - - private: - mojo::Remote<mojom::UDPSocket>* socket_; -}; - -// An implementation of mojom::UDPSocketListener that records received results. -class UDPSocketListenerImpl : public mojom::UDPSocketListener { - public: - struct ReceivedResult { - ReceivedResult(int net_error_arg, - const base::Optional<net::IPEndPoint>& src_addr_arg, - base::Optional<std::vector<uint8_t>> data_arg); - ReceivedResult(const ReceivedResult& other); - ~ReceivedResult(); - - int net_error; - base::Optional<net::IPEndPoint> src_addr; - base::Optional<std::vector<uint8_t>> data; - }; - - UDPSocketListenerImpl(); - ~UDPSocketListenerImpl() override; - - const std::vector<ReceivedResult>& results() const { return results_; } - - void WaitForReceivedResults(size_t count); - - private: - void OnReceived(int32_t result, - const base::Optional<net::IPEndPoint>& src_addr, - base::Optional<base::span<const uint8_t>> data) override; - std::unique_ptr<base::RunLoop> run_loop_; - std::vector<ReceivedResult> results_; - size_t expected_receive_count_; - - DISALLOW_COPY_AND_ASSIGN(UDPSocketListenerImpl); -}; - -} // namespace test - -} // namespace network - -#endif // SERVICES_NETWORK_UDP_SOCKET_TEST_UTIL_H_ diff --git a/chromium/services/network/udp_socket_unittest.cc b/chromium/services/network/udp_socket_unittest.cc index 8601d120fe0..6c180f8f335 100644 --- a/chromium/services/network/udp_socket_unittest.cc +++ b/chromium/services/network/udp_socket_unittest.cc @@ -26,7 +26,7 @@ #include "net/url_request/url_request_test_util.h" #include "services/network/public/mojom/udp_socket.mojom.h" #include "services/network/socket_factory.h" -#include "services/network/udp_socket_test_util.h" +#include "services/network/test/udp_socket_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace network { diff --git a/chromium/services/network/url_loader.cc b/chromium/services/network/url_loader.cc index 9d2d455613f..0c19f0441db 100644 --- a/chromium/services/network/url_loader.cc +++ b/chromium/services/network/url_loader.cc @@ -28,10 +28,10 @@ #include "base/trace_event/trace_event.h" #include "mojo/public/cpp/system/simple_watcher.h" #include "net/base/elements_upload_data_stream.h" -#include "net/base/ip_endpoint.h" #include "net/base/isolation_info.h" #include "net/base/load_flags.h" #include "net/base/mime_sniffer.h" +#include "net/base/transport_info.h" #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_file_element_reader.h" #include "net/cookies/canonical_cookie.h" @@ -41,9 +41,11 @@ #include "net/ssl/ssl_connection_status_flags.h" #include "net/ssl/ssl_private_key.h" #include "net/traffic_annotation/network_traffic_annotation.h" +#include "net/url_request/redirect_info.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "services/network/chunked_data_pipe_upload_data_stream.h" +#include "services/network/cross_origin_read_blocking_exception_for_plugin.h" #include "services/network/data_pipe_element_reader.h" #include "services/network/network_usage_accumulator.h" #include "services/network/origin_policy/origin_policy_constants.h" @@ -52,6 +54,7 @@ #include "services/network/public/cpp/cross_origin_resource_policy.h" #include "services/network/public/cpp/empty_url_loader_client.h" #include "services/network/public/cpp/header_util.h" +#include "services/network/public/cpp/ip_address_space_util.h" #include "services/network/public/cpp/net_adapters.h" #include "services/network/public/cpp/network_switches.h" #include "services/network/public/cpp/origin_policy.h" @@ -207,12 +210,14 @@ std::unique_ptr<net::UploadDataStream> CreateUploadDataStream( std::vector<base::File>& opened_files, base::SequencedTaskRunner* file_task_runner) { // In the case of a chunked upload, there will just be one element. - if (body->elements()->size() == 1 && - body->elements()->begin()->type() == - network::mojom::DataElementType::kChunkedDataPipe) { - return std::make_unique<ChunkedDataPipeUploadDataStream>( - body, const_cast<DataElement&>(body->elements()->front()) - .ReleaseChunkedDataPipeGetter()); + if (body->elements()->size() == 1) { + network::mojom::DataElementType type = body->elements()->begin()->type(); + if (type == network::mojom::DataElementType::kChunkedDataPipe || + type == network::mojom::DataElementType::kReadOnceStream) { + return std::make_unique<ChunkedDataPipeUploadDataStream>( + body, + body->elements_mutable()->begin()->ReleaseChunkedDataPipeGetter()); + } } auto opened_file = opened_files.begin(); @@ -241,7 +246,8 @@ std::unique_ptr<net::UploadDataStream> CreateUploadDataStream( body, element.CloneDataPipeGetter())); break; } - case network::mojom::DataElementType::kChunkedDataPipe: { + case network::mojom::DataElementType::kChunkedDataPipe: + case network::mojom::DataElementType::kReadOnceStream: { // This shouldn't happen, as the traits logic should ensure that if // there's a chunked pipe, there's one and only one element. NOTREACHED(); @@ -594,7 +600,7 @@ URLLoader::URLLoader( is_nocors_corb_excluded_request_ = request.corb_excluded && request.mode == mojom::RequestMode::kNoCors && - CrossOriginReadBlocking::ShouldAllowForPlugin( + CrossOriginReadBlockingExceptionForPlugin::ShouldAllowForPlugin( factory_params_->process_id); request_mode_ = request.mode; if (request.trusted_params) { @@ -612,7 +618,7 @@ URLLoader::URLLoader( if (request.update_first_party_url_on_redirect) { url_request_->set_first_party_url_policy( - net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); + net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT); } url_request_->SetLoadFlags(request.load_flags); @@ -979,6 +985,65 @@ void URLLoader::ResumeReadingBodyFromNet() { } } +int URLLoader::CanConnectToRemoteEndpoint( + const net::TransportInfo& info) const { + const mojom::IPAddressSpace remote_address_space = + IPAddressToIPAddressSpace(info.endpoint.address()); + if (options_ & mojom::kURLLoadOptionBlockLocalRequest && + IsLessPublicAddressSpace(remote_address_space, + network::mojom::IPAddressSpace::kPublic)) { + DVLOG(1) << "CORS-RFC1918 check: failed due to loader options."; + return net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST; + } + + const mojom::ClientSecurityStatePtr& security_state = + factory_params_->client_security_state; + if (!security_state) { + DVLOG(1) << "CORS-RFC1918 check: skipped, missing client security state."; + return net::OK; + } + + DVLOG(1) << "CORS-RFC1918 check: running against client security state = { " + << "is_web_secure_context: " << security_state->is_web_secure_context + << ", ip_address_space: " << security_state->ip_address_space + << ", private_network_request_policy: " + << security_state->private_network_request_policy << " }."; + + // We use a switch statement to force this code to be amended when values are + // added to the PrivateNetworkRequestPolicy enum. + switch (security_state->private_network_request_policy) { + case mojom::PrivateNetworkRequestPolicy::kAllow: + break; + case mojom::PrivateNetworkRequestPolicy::kBlockFromInsecureToMorePrivate: + // We block requests from insecure contexts to resources in IP address + // spaces more private than that of the initiator. This prevents malicious + // insecure public websites from making requests to someone's printer, for + // example. + if (!security_state->is_web_secure_context && + IsLessPublicAddressSpace(remote_address_space, + security_state->ip_address_space)) { + DVLOG(1) << "CORS-RFC1918 check: " + "failed, blocking insecure private network request."; + return net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST; + } + } + + DVLOG(1) << "CORS-RFC1918 check: success."; + return net::OK; +} + +int URLLoader::OnConnected(net::URLRequest* url_request, + const net::TransportInfo& info) { + DCHECK_EQ(url_request, url_request_.get()); + + DVLOG(1) << "Connection obtained for URL request to " << url_request->url() + << ": " << info; + + // Now that the request endpoint's address has been resolved, check if + // this request should be blocked by CORS-RFC1918 rules. + return CanConnectToRemoteEndpoint(info); +} + void URLLoader::OnReceivedRedirect(net::URLRequest* url_request, const net::RedirectInfo& redirect_info, bool* defer_redirect) { @@ -1015,7 +1080,7 @@ void URLLoader::OnReceivedRedirect(net::URLRequest* url_request, CrossOriginResourcePolicy::IsBlocked( url_request_->url(), url_request_->original_url(), url_request_->initiator(), *response, request_mode_, - factory_params_->request_initiator_site_lock, + factory_params_->request_initiator_origin_lock, cross_origin_embedder_policy, coep_reporter_)) { CompleteBlockedResponse(net::ERR_BLOCKED_BY_RESPONSE, false, blocked_reason); @@ -1035,22 +1100,13 @@ void URLLoader::OnReceivedRedirect(net::URLRequest* url_request, // static bool URLLoader::HasFetchStreamingUploadBody(const ResourceRequest* request) { - // Follows blink::mojom::ResourceType::kXhr. - const int kXhr = 13; - if (request->resource_type != kXhr) - return false; const ResourceRequestBody* request_body = request->request_body.get(); if (!request_body) return false; const std::vector<DataElement>* elements = request_body->elements(); - if (elements->size() == 0u) - return false; - // https://fetch.spec.whatwg.org/#concept-bodyinit-extract - // Body's source is null means the body is not extracted from ReadableStream. - // See blink::PopulateResourceRequest() for actual construction. - if (elements->size() > 1u) + if (elements->size() != 1u) return false; - return elements->at(0).type() == mojom::DataElementType::kChunkedDataPipe; + return elements->front().type() == mojom::DataElementType::kReadOnceStream; } void URLLoader::OnAuthRequired(net::URLRequest* url_request, @@ -1153,6 +1209,44 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { return; } + response_ = network::mojom::URLResponseHead::New(); + PopulateResourceResponse( + url_request_.get(), is_load_timing_enabled_, + options_ & mojom::kURLLoadOptionSendSSLInfoWithResponse, response_.get()); + if (report_raw_headers_) { + response_->raw_request_response_info = BuildRawRequestResponseInfo( + *url_request_, raw_request_headers_, raw_response_headers_.get()); + raw_request_headers_ = net::HttpRawRequestHeaders(); + raw_response_headers_ = nullptr; + } + + // Parse and remove the Trust Tokens response headers, if any are expected, + // potentially failing the request if an error occurs. + if (trust_token_helper_) { + trust_token_helper_->Finalize( + response_.get(), + base::BindOnce(&URLLoader::OnDoneFinalizingTrustTokenOperation, + weak_ptr_factory_.GetWeakPtr())); + // |this| may have been deleted. + return; + } + + ContinueOnResponseStarted(); +} + +void URLLoader::OnDoneFinalizingTrustTokenOperation( + mojom::TrustTokenOperationStatus status) { + trust_token_status_ = status; + + if (status != mojom::TrustTokenOperationStatus::kOk) { + NotifyCompleted(net::ERR_TRUST_TOKEN_OPERATION_FAILED); + // |this| may have been deleted. + return; + } + ContinueOnResponseStarted(); +} + +void URLLoader::ContinueOnResponseStarted() { MojoCreateDataPipeOptions options; options.struct_size = sizeof(MojoCreateDataPipeOptions); options.flags = MOJO_CREATE_DATA_PIPE_FLAG_NONE; @@ -1178,17 +1272,6 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { upload_progress_tracker_ = nullptr; } - response_ = network::mojom::URLResponseHead::New(); - PopulateResourceResponse( - url_request_.get(), is_load_timing_enabled_, - options_ & mojom::kURLLoadOptionSendSSLInfoWithResponse, response_.get()); - if (report_raw_headers_) { - response_->raw_request_response_info = BuildRawRequestResponseInfo( - *url_request_, raw_request_headers_, raw_response_headers_.get()); - raw_request_headers_ = net::HttpRawRequestHeaders(); - raw_response_headers_ = nullptr; - } - peer_closed_handle_watcher_.Watch( response_body_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED, base::BindRepeating(&URLLoader::OnResponseBodyStreamConsumerClosed, @@ -1210,7 +1293,7 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { CrossOriginResourcePolicy::IsBlocked( url_request_->url(), url_request_->original_url(), url_request_->initiator(), *response_, request_mode_, - factory_params_->request_initiator_site_lock, + factory_params_->request_initiator_origin_lock, cross_origin_embedder_policy, coep_reporter_)) { CompleteBlockedResponse(net::ERR_BLOCKED_BY_RESPONSE, false, blocked_reason); @@ -1218,34 +1301,6 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { return; } - // Parse and remove the Trust Tokens response headers, if any are expected, - // potentially failing the request if an error occurs. - if (trust_token_helper_) { - trust_token_helper_->Finalize( - response_.get(), - base::BindOnce( - [](base::WeakPtr<URLLoader> loader, net::URLRequest* request, - int net_error, mojom::TrustTokenOperationStatus status) { - if (!loader) - return; - if (status != mojom::TrustTokenOperationStatus::kOk) { - loader->trust_token_status_ = status; - loader->NotifyCompleted(net::ERR_TRUST_TOKEN_OPERATION_FAILED); - // |loader| may have been deleted. - return; - } - loader->ContinueOnResponseStarted(request, net_error); - }, - weak_ptr_factory_.GetWeakPtr(), url_request, net_error)); - // |this| may have been deleted. - return; - } - - ContinueOnResponseStarted(url_request, net_error); -} - -void URLLoader::ContinueOnResponseStarted(net::URLRequest* url_request, - int net_error) { // Figure out if we need to sniff (for MIME type detection or for Cross-Origin // Read Blocking / CORB). if (factory_params_->is_corb_enabled && !is_nocors_corb_excluded_request_) { @@ -1255,7 +1310,7 @@ void URLLoader::ContinueOnResponseStarted(net::URLRequest* url_request, corb_analyzer_ = std::make_unique<CrossOriginReadBlocking::ResponseAnalyzer>( url_request_->url(), url_request_->initiator(), *response_, - factory_params_->request_initiator_site_lock, request_mode_, + factory_params_->request_initiator_origin_lock, request_mode_, isolated_world_origin_, network_service_client_); is_more_corb_sniffing_needed_ = corb_analyzer_->needs_sniffing(); if (corb_analyzer_->ShouldBlock()) { @@ -1302,8 +1357,8 @@ void URLLoader::ContinueOnResponseStarted(net::URLRequest* url_request, // request of the original URL. net::IsolationInfo isolation_info = net::IsolationInfo::Create( net::IsolationInfo::RedirectMode::kUpdateNothing, - url_request->isolation_info().top_frame_origin().value(), - url_request->isolation_info().frame_origin().value(), + url_request_->isolation_info().top_frame_origin().value(), + url_request_->isolation_info().frame_origin().value(), net::SiteForCookies()); origin_policy_manager_->RetrieveOriginPolicy( url::Origin::Create(url_request_->url()), isolation_info, @@ -1794,14 +1849,13 @@ void URLLoader::SetRawRequestHeadersAndNotify( } if (cookie_observer_) { - net::CookieStatusList reported_cookies; + net::CookieAccessResultList reported_cookies; for (const auto& cookie_with_access_result : url_request_->maybe_sent_cookies()) { if (ShouldNotifyAboutCookie( cookie_with_access_result.access_result.status)) { - reported_cookies.push_back( - {cookie_with_access_result.cookie, - cookie_with_access_result.access_result.status}); + reported_cookies.push_back({cookie_with_access_result.cookie, + cookie_with_access_result.access_result}); } } @@ -2009,13 +2063,15 @@ void URLLoader::ReportFlaggedResponseCookies() { } if (cookie_observer_) { - net::CookieStatusList reported_cookies; - for (const auto& cookie_line_and_status : + net::CookieAccessResultList reported_cookies; + for (const auto& cookie_line_and_access_result : url_request_->maybe_stored_cookies()) { - if (ShouldNotifyAboutCookie(cookie_line_and_status.status) && - cookie_line_and_status.cookie) { - reported_cookies.push_back({cookie_line_and_status.cookie.value(), - cookie_line_and_status.status}); + if (ShouldNotifyAboutCookie( + cookie_line_and_access_result.access_result.status) && + cookie_line_and_access_result.cookie) { + reported_cookies.push_back( + {cookie_line_and_access_result.cookie.value(), + cookie_line_and_access_result.access_result}); } } diff --git a/chromium/services/network/url_loader.h b/chromium/services/network/url_loader.h index 33e6b100ef3..a624b8c43f4 100644 --- a/chromium/services/network/url_loader.h +++ b/chromium/services/network/url_loader.h @@ -27,9 +27,9 @@ #include "net/http/http_raw_request_headers.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/url_request.h" -#include "services/network/cross_origin_read_blocking.h" #include "services/network/keepalive_statistics_recorder.h" #include "services/network/network_service.h" +#include "services/network/public/cpp/cross_origin_read_blocking.h" #include "services/network/public/cpp/initiator_lock_compatibility.h" #include "services/network/public/mojom/cookie_access_observer.mojom.h" #include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h" @@ -46,6 +46,9 @@ namespace net { class HttpResponseHeaders; +class IPEndPoint; +struct RedirectInfo; +struct TransportInfo; class URLRequestContext; } // namespace net @@ -133,6 +136,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader void ResumeReadingBodyFromNet() override; // net::URLRequest::Delegate implementation: + int OnConnected(net::URLRequest* url_request, + const net::TransportInfo& info) override; void OnReceivedRedirect(net::URLRequest* url_request, const net::RedirectInfo& redirect_info, bool* defer_redirect) override; @@ -265,7 +270,15 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader // - receive the result in OnDoneBeginningTrustTokenOperation; // - if successful, ScheduleStart; if there was an error, fail. // - // (Inbound, response handling just takes a synchronous Finalize call.) + // Inbound control flow: + // + // Start in OnResponseStarted + // - If there are no Trust Tokens parameters, proceed to + // ContinueOnResponseStarted. + // - Otherwise: + // - execute TrustTokenRequestHelper::Finalize against the helper; + // - receive the result in OnDoneFinalizingTrusttokenOperation; + // - if successful, ContinueOnResponseStarted; if there was an error, fail. void BeginTrustTokenOperationIfNecessaryAndThenScheduleStart( const ResourceRequest& request); void OnDoneConstructingTrustTokenHelper( @@ -273,9 +286,11 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader TrustTokenStatusOrRequestHelper status_or_helper); void OnDoneBeginningTrustTokenOperation( mojom::TrustTokenOperationStatus status); + void OnDoneFinalizingTrustTokenOperation( + mojom::TrustTokenOperationStatus status); // Continuation of |OnResponseStarted| after possibly asynchronously // concluding the request's Trust Tokens operation. - void ContinueOnResponseStarted(net::URLRequest* url_request, int net_error); + void ContinueOnResponseStarted(); void ScheduleStart(); void ReadMore(); @@ -330,6 +345,16 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader void StartReading(); void OnOriginPolicyManagerRetrieveDone(const OriginPolicy& origin_policy); + // Checks if the request initiator should be allowed to make requests to the + // remote endpoint, as described in |info|. + // + // Returns a net error code. + // + // See the CORS-RFC1918 spec: https://wicg.github.io/cors-rfc1918. + // + // Helper for OnConnected(). + int CanConnectToRemoteEndpoint(const net::TransportInfo& info) const; + net::URLRequestContext* url_request_context_; mojom::NetworkServiceClient* network_service_client_; mojom::NetworkContextClient* network_context_client_; diff --git a/chromium/services/network/url_loader_factory.cc b/chromium/services/network/url_loader_factory.cc index c8f02a3760f..75ae8488efa 100644 --- a/chromium/services/network/url_loader_factory.cc +++ b/chromium/services/network/url_loader_factory.cc @@ -215,22 +215,6 @@ void URLLoaderFactory::CreateLoaderAndStart( return; } - if (url_request.trust_token_params && !context_->trust_token_store()) { - mojo::ReportBadMessage( - "Got a request with Trust Tokens parameters with Trust tokens " - "disabled."); - return; - } - - if (url_request.trust_token_params && url_request.request_initiator && - !IsOriginPotentiallyTrustworthy(*url_request.request_initiator)) { - mojo::ReportBadMessage( - "Got a request with Trust Tokens parameters from an insecure context, " - "but Trust Tokens operations may only be executed from secure " - "contexts."); - return; - } - std::unique_ptr<TrustTokenRequestHelperFactory> trust_token_factory; if (url_request.trust_token_params) { trust_token_factory = std::make_unique<TrustTokenRequestHelperFactory>( diff --git a/chromium/services/network/url_loader_unittest.cc b/chromium/services/network/url_loader_unittest.cc index dc8375fd926..72b18010175 100644 --- a/chromium/services/network/url_loader_unittest.cc +++ b/chromium/services/network/url_loader_unittest.cc @@ -43,12 +43,15 @@ #include "net/base/escape.h" #include "net/base/features.h" #include "net/base/io_buffer.h" +#include "net/base/ip_endpoint.h" #include "net/base/isolation_info.h" #include "net/base/load_flags.h" #include "net/base/mime_sniffer.h" #include "net/base/net_errors.h" +#include "net/base/transport_info.h" #include "net/cert/internal/parse_name.h" #include "net/cert/test_root_certs.h" +#include "net/cookies/cookie_access_result.h" #include "net/cookies/cookie_change_dispatcher.h" #include "net/cookies/cookie_inclusion_status.h" #include "net/cookies/cookie_util.h" @@ -60,6 +63,7 @@ #include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_response.h" +#include "net/test/gtest_util.h" #include "net/test/quic_simple_test_server.h" #include "net/test/test_data_directory.h" #include "net/test/url_request/url_request_failed_job.h" @@ -70,13 +74,14 @@ #include "net/url_request/url_request_filter.h" #include "net/url_request/url_request_interceptor.h" #include "net/url_request/url_request_job.h" -#include "net/url_request/url_request_status.h" #include "net/url_request/url_request_test_job.h" #include "net/url_request/url_request_test_util.h" +#include "services/network/cross_origin_read_blocking_exception_for_plugin.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/mojom/cookie_access_observer.mojom-forward.h" #include "services/network/public/mojom/cookie_access_observer.mojom.h" +#include "services/network/public/mojom/ip_address_space.mojom.h" #include "services/network/public/mojom/origin_policy_manager.mojom.h" #include "services/network/public/mojom/trust_tokens.mojom-shared.h" #include "services/network/public/mojom/url_loader.mojom.h" @@ -99,7 +104,10 @@ namespace network { namespace { +using ::net::test::IsError; +using ::net::test::IsOk; using ::testing::Optional; +using ::testing::ValuesIn; // Returns a URLLoader::DeleteCallback that destroys |url_loader| and quits // |run_loop| when invoked. Tests must wait on the RunLoop to ensure nothing is @@ -379,6 +387,89 @@ class SimulatedCacheInterceptor : public net::URLRequestInterceptor { DISALLOW_COPY_AND_ASSIGN(SimulatedCacheInterceptor); }; +// Fakes the TransportInfo passed to URLRequest::Delegate::OnConnected(). +class URLRequestFakeTransportInfoJob : public net::URLRequestJob { + public: + // |transport_info| is subsequently passed to the OnConnected() callback. + URLRequestFakeTransportInfoJob(net::URLRequest* request, + net::NetworkDelegate* network_delegate, + const net::TransportInfo& transport_info) + : URLRequestJob(request, network_delegate), + transport_info_(transport_info) {} + + URLRequestFakeTransportInfoJob(const URLRequestFakeTransportInfoJob&) = + delete; + URLRequestFakeTransportInfoJob& operator=( + const URLRequestFakeTransportInfoJob&) = delete; + + // net::URLRequestJob implementation: + void Start() override { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&URLRequestFakeTransportInfoJob::StartAsync, + weak_factory_.GetWeakPtr())); + } + + int ReadRawData(net::IOBuffer* buf, int buf_size) override { return 0; } + + private: + ~URLRequestFakeTransportInfoJob() override = default; + + void StartAsync() { + const int result = NotifyConnected(transport_info_); + if (result != net::OK) { + NotifyStartError(result); + return; + } + + NotifyHeadersComplete(); + } + + // The fake transport info we pass to OnConnected(). + const net::TransportInfo transport_info_; + + base::WeakPtrFactory<URLRequestFakeTransportInfoJob> weak_factory_{this}; +}; + +// Intercepts URLRequestJob creation to a specific URL. All requests to this +// URL will report being connected with a fake TransportInfo struct. +class FakeTransportInfoInterceptor : public net::URLRequestInterceptor { + public: + // All intercepted requests will claim to be connected via |transport_info|. + explicit FakeTransportInfoInterceptor( + const net::TransportInfo& transport_info) + : transport_info_(transport_info) {} + + ~FakeTransportInfoInterceptor() override = default; + + FakeTransportInfoInterceptor(const FakeTransportInfoInterceptor&) = delete; + FakeTransportInfoInterceptor& operator=(const FakeTransportInfoInterceptor&) = + delete; + + // URLRequestInterceptor implementation: + net::URLRequestJob* MaybeInterceptRequest( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const override { + // NOTE: We cannot use make_unique() because + // URLRequestFakeTransportInfoJob's destructor is private, which prevents + // use of unique_ptr. + return new URLRequestFakeTransportInfoJob(request, network_delegate, + transport_info_); + } + + private: + const net::TransportInfo transport_info_; +}; + +// Returns a maximally-restrictive security state for use in tests. +mojom::ClientSecurityStatePtr NewSecurityState() { + auto result = mojom::ClientSecurityState::New(); + result->is_web_secure_context = false; + result->private_network_request_policy = + mojom::PrivateNetworkRequestPolicy::kBlockFromInsecureToMorePrivate; + result->ip_address_space = mojom::IPAddressSpace::kUnknown; + return result; +} + // Returns whether monitoring was successfully set up. If yes, // StopMonitorBodyReadFromNetBeforePausedHistogram() needs to be called later to // stop monitoring. @@ -491,9 +582,12 @@ class URLLoaderTest : public testing::Test { base::RunLoop delete_run_loop; mojo::Remote<mojom::URLLoader> loader; std::unique_ptr<URLLoader> url_loader; - static mojom::URLLoaderFactoryParams params; + + mojom::URLLoaderFactoryParams params; params.process_id = mojom::kBrowserProcessId; params.is_corb_enabled = false; + params.client_security_state.Swap(&client_security_state_); + url::Origin origin = url::Origin::Create(url); params.isolation_info = net::IsolationInfo::CreateForInternalRequest(origin); @@ -668,6 +762,9 @@ class URLLoaderTest : public testing::Test { DCHECK(!ran_); expect_redirect_ = true; } + void set_client_security_state(mojom::ClientSecurityStatePtr state) { + client_security_state_ = std::move(state); + } void set_request_body(scoped_refptr<ResourceRequestBody> request_body) { request_body_ = request_body; } @@ -791,6 +888,7 @@ class URLLoaderTest : public testing::Test { bool send_ssl_with_response_ = false; bool send_ssl_for_cert_error_ = false; bool expect_redirect_ = false; + mojom::ClientSecurityStatePtr client_security_state_; scoped_refptr<ResourceRequestBody> request_body_; // Used to ensure that methods are called either before or after a request is @@ -838,6 +936,307 @@ TEST_F(URLLoaderTest, SSLSentOnlyWhenRequested) { ASSERT_FALSE(!!ssl_info()); } +// This test verifies that when the URLLoaderFactory's parameters are missing +// a client security state, requests to local network resources are authorized. +TEST_F(URLLoaderTest, MissingClientSecurityStateIsOk) { + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +// These tests verify that requests from an secure page to an IP in the +// "local" address space are never blocked. + +TEST_F(URLLoaderTest, SecureUnknownToLocalIsOk) { + auto client_security_state = NewSecurityState(); + client_security_state->is_web_secure_context = true; + client_security_state->ip_address_space = mojom::IPAddressSpace::kUnknown; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +TEST_F(URLLoaderTest, SecurePublicToLocalIsOk) { + auto client_security_state = NewSecurityState(); + client_security_state->is_web_secure_context = true; + client_security_state->ip_address_space = mojom::IPAddressSpace::kPublic; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +TEST_F(URLLoaderTest, SecurePrivateToLocalIsBlocked) { + auto client_security_state = NewSecurityState(); + client_security_state->is_web_secure_context = true; + client_security_state->ip_address_space = mojom::IPAddressSpace::kPrivate; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +TEST_F(URLLoaderTest, SecureLocalToLocalIsOk) { + auto client_security_state = NewSecurityState(); + client_security_state->is_web_secure_context = true; + client_security_state->ip_address_space = mojom::IPAddressSpace::kLocal; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +// These tests verify that requests from any page to an IP in the "local" +// address space are never blocked when the request policy is kAllow. + +TEST_F(URLLoaderTest, PolicyIsAllowUnknownToLocalIsOk) { + auto client_security_state = NewSecurityState(); + client_security_state->private_network_request_policy = + mojom::PrivateNetworkRequestPolicy::kAllow; + client_security_state->ip_address_space = mojom::IPAddressSpace::kUnknown; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +TEST_F(URLLoaderTest, PolicyIsAllowPublicToLocalIsOk) { + auto client_security_state = NewSecurityState(); + client_security_state->private_network_request_policy = + mojom::PrivateNetworkRequestPolicy::kAllow; + client_security_state->ip_address_space = mojom::IPAddressSpace::kPublic; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +TEST_F(URLLoaderTest, PolicyIsAllowPrivateToLocalIsBlocked) { + auto client_security_state = NewSecurityState(); + client_security_state->private_network_request_policy = + mojom::PrivateNetworkRequestPolicy::kAllow; + client_security_state->ip_address_space = mojom::IPAddressSpace::kPrivate; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +TEST_F(URLLoaderTest, PolicyIsAllowLocalToLocalIsOk) { + auto client_security_state = NewSecurityState(); + client_security_state->private_network_request_policy = + mojom::PrivateNetworkRequestPolicy::kAllow; + client_security_state->ip_address_space = mojom::IPAddressSpace::kLocal; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +// These tests verify that requests from an insecure page to an IP in the +// "local" address space are blocked unless the page came from the same address +// space. In practice, the local address space contains only localhost. +// +// NOTE: These tests exercise the same codepath as +// URLLoaderFakeTransportInfoTest below, except they use real URLRequestJob and +// HttpTransaction implementations for higher confidence in the correctness of +// the whole stack. OTOH, using an embedded test server prevents us from mocking +// out the endpoint IP address. + +TEST_F(URLLoaderTest, InsecureRequestToLocalResource) { + auto client_security_state = NewSecurityState(); + client_security_state->is_web_secure_context = false; + client_security_state->ip_address_space = mojom::IPAddressSpace::kUnknown; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), + IsError(net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST)); +} + +TEST_F(URLLoaderTest, InsecurePublicToLocalIsBlocked) { + auto client_security_state = NewSecurityState(); + client_security_state->is_web_secure_context = false; + client_security_state->ip_address_space = mojom::IPAddressSpace::kPublic; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), + IsError(net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST)); +} + +TEST_F(URLLoaderTest, InsecurePrivateToLocalIsBlocked) { + auto client_security_state = NewSecurityState(); + client_security_state->is_web_secure_context = false; + client_security_state->ip_address_space = mojom::IPAddressSpace::kPrivate; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), + IsError(net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST)); +} + +TEST_F(URLLoaderTest, InsecureLocalToLocalIsOk) { + auto client_security_state = NewSecurityState(); + client_security_state->is_web_secure_context = false; + client_security_state->ip_address_space = mojom::IPAddressSpace::kLocal; + set_client_security_state(std::move(client_security_state)); + + EXPECT_THAT(Load(test_server()->GetURL("/empty.html")), IsOk()); +} + +// Bundles together the inputs to a parameterized private network request test. +struct URLLoaderFakeTransportInfoTestParams { + // The address space of the client. + mojom::IPAddressSpace client_address_space; + + // The address space of the endpoint serving the request. + mojom::IPAddressSpace endpoint_address_space; + + // The expected request result. + int expected_result; +}; + +// For clarity when debugging parameterized test failures. +std::ostream& operator<<(std::ostream& out, + const URLLoaderFakeTransportInfoTestParams& params) { + return out << "{ client_address_space: " << params.client_address_space + << ", endpoint_address_space: " << params.endpoint_address_space + << ", expected_result: " + << net::ErrorToString(params.expected_result) << " }"; +} + +class URLLoaderFakeTransportInfoTest + : public URLLoaderTest, + public testing::WithParamInterface<URLLoaderFakeTransportInfoTestParams> { + protected: + // Returns an address in the given IP address space. + static net::IPAddress FakeAddress(mojom::IPAddressSpace space) { + switch (space) { + case mojom::IPAddressSpace::kUnknown: + return net::IPAddress(); + case mojom::IPAddressSpace::kPublic: + return net::IPAddress(42, 0, 1, 2); + case mojom::IPAddressSpace::kPrivate: + return net::IPAddress(10, 0, 1, 2); + case mojom::IPAddressSpace::kLocal: + return net::IPAddress::IPv4Localhost(); + } + } + + // Returns an endpoint in the given IP address space. + static net::IPEndPoint FakeEndpoint(mojom::IPAddressSpace space) { + return net::IPEndPoint(FakeAddress(space), 80); + } + + // Returns a transport info with an endpoint in the given IP address space. + static net::TransportInfo FakeTransportInfo(mojom::IPAddressSpace space) { + return net::TransportInfo(net::TransportType::kDirect, FakeEndpoint(space)); + } +}; + +// This test verifies that requests made from insecure contexts are handled +// appropriately when they go from a less-private address space to a +// more-private address space or not. The test is parameterized by +// (client address space, server address space, expected result) tuple. +TEST_P(URLLoaderFakeTransportInfoTest, PrivateNetworkRequestLoadsCorrectly) { + const auto params = GetParam(); + + auto client_security_state = NewSecurityState(); + client_security_state->ip_address_space = params.client_address_space; + set_client_security_state(std::move(client_security_state)); + + const GURL url("http://fake-endpoint"); + + net::URLRequestFilter::GetInstance()->AddUrlInterceptor( + url, std::make_unique<FakeTransportInfoInterceptor>( + FakeTransportInfo(params.endpoint_address_space))); + + // Despite its name, IsError(OK) asserts that the matched value is OK. + EXPECT_THAT(Load(url), IsError(params.expected_result)); +} + +// Lists all combinations we want to test in URLLoaderFakeTransportInfoTest. +constexpr URLLoaderFakeTransportInfoTestParams + kURLLoaderFakeTransportInfoTestParamsList[] = { + // Client: kUnknown + { + mojom::IPAddressSpace::kUnknown, + mojom::IPAddressSpace::kUnknown, + net::OK, + }, + { + mojom::IPAddressSpace::kUnknown, + mojom::IPAddressSpace::kPublic, + net::OK, + }, + { + mojom::IPAddressSpace::kUnknown, + mojom::IPAddressSpace::kPrivate, + net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST, + }, + { + mojom::IPAddressSpace::kUnknown, + mojom::IPAddressSpace::kLocal, + net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST, + }, + // Client: kPublic + { + mojom::IPAddressSpace::kPublic, + mojom::IPAddressSpace::kUnknown, + net::OK, + }, + { + mojom::IPAddressSpace::kPublic, + mojom::IPAddressSpace::kPublic, + net::OK, + }, + { + mojom::IPAddressSpace::kPublic, + mojom::IPAddressSpace::kPrivate, + net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST, + }, + { + mojom::IPAddressSpace::kPublic, + mojom::IPAddressSpace::kLocal, + net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST, + }, + // Client: kPrivate + { + mojom::IPAddressSpace::kPrivate, + mojom::IPAddressSpace::kUnknown, + net::OK, + }, + { + mojom::IPAddressSpace::kPrivate, + mojom::IPAddressSpace::kPublic, + net::OK, + }, + { + mojom::IPAddressSpace::kPrivate, + mojom::IPAddressSpace::kPrivate, + net::OK, + }, + { + mojom::IPAddressSpace::kPrivate, + mojom::IPAddressSpace::kLocal, + net::ERR_INSECURE_PRIVATE_NETWORK_REQUEST, + }, + // Client: kLocal + { + mojom::IPAddressSpace::kLocal, + mojom::IPAddressSpace::kUnknown, + net::OK, + }, + { + mojom::IPAddressSpace::kLocal, + mojom::IPAddressSpace::kPublic, + net::OK, + }, + { + mojom::IPAddressSpace::kLocal, + mojom::IPAddressSpace::kPrivate, + net::OK, + }, + { + mojom::IPAddressSpace::kLocal, + mojom::IPAddressSpace::kLocal, + net::OK, + }, +}; + +INSTANTIATE_TEST_SUITE_P(Parameterized, + URLLoaderFakeTransportInfoTest, + ValuesIn(kURLLoaderFakeTransportInfoTestParamsList)); + // Tests that auth challenge info is present on the response when a request // receives an authentication challenge. TEST_F(URLLoaderTest, AuthChallengeInfo) { @@ -1874,6 +2273,62 @@ TEST_F(URLLoaderTest, UploadChunkedDataPipe) { EXPECT_EQ(net::OK, client()->completion_status().error_code); } +// Tests a request body with ReadOnceStream. +TEST_F(URLLoaderTest, UploadReadOnceStream) { + const std::string kRequestBody = "Request Body"; + + TestChunkedDataPipeGetter data_pipe_getter; + + ResourceRequest request = + CreateResourceRequest("POST", test_server()->GetURL("/echo")); + request.request_body = base::MakeRefCounted<ResourceRequestBody>(); + request.request_body->SetToReadOnceStream( + data_pipe_getter.GetDataPipeGetterRemote()); + + base::HistogramTester tester; + std::string histogram_allowh1("Net.Fetch.UploadStreamingProtocolAllowH1"); + std::string histogram_notallowh1( + "Net.Fetch.UploadStreamingProtocolNotAllowH1"); + tester.ExpectTotalCount(histogram_allowh1, 0); + tester.ExpectTotalCount(histogram_notallowh1, 0); + + base::RunLoop delete_run_loop; + mojo::Remote<mojom::URLLoader> loader; + std::unique_ptr<URLLoader> url_loader; + mojom::URLLoaderFactoryParams params; + params.process_id = mojom::kBrowserProcessId; + params.is_corb_enabled = false; + url_loader = std::make_unique<URLLoader>( + context(), nullptr /* network_service_client */, + nullptr /* network_context_client */, + DeleteLoaderCallback(&delete_run_loop, &url_loader), + loader.BindNewPipeAndPassReceiver(), 0, request, client()->CreateRemote(), + /*reponse_body_use_tracker=*/base::nullopt, TRAFFIC_ANNOTATION_FOR_TESTS, + ¶ms, /*coep_reporter=*/nullptr, 0 /* request_id */, + 0 /* keepalive_request_size */, nullptr /* resource_scheduler_client */, + nullptr /* keepalive_statistics_reporter */, + nullptr /* network_usage_accumulator */, nullptr /* header_client */, + nullptr /* origin_policy_manager */, nullptr /* trust_token_helper */, + mojo::NullRemote() /* cookie_observer */); + + mojom::ChunkedDataPipeGetter::GetSizeCallback get_size_callback = + data_pipe_getter.WaitForGetSize(); + mojo::BlockingCopyFromString(kRequestBody, + data_pipe_getter.WaitForStartReading()); + std::move(get_size_callback).Run(net::OK, kRequestBody.size()); + delete_run_loop.Run(); + client()->RunUntilComplete(); + + EXPECT_EQ(kRequestBody, ReadBody()); + EXPECT_EQ(net::OK, client()->completion_status().error_code); + + tester.ExpectTotalCount(histogram_allowh1, 1); + // From ReportFetchUploadStreamingUMA() + constexpr int kHTTP1_1 = 0; + tester.ExpectBucketCount(histogram_allowh1, kHTTP1_1, 1); + tester.ExpectTotalCount(histogram_notallowh1, 0); +} + // Tests that SSLInfo is not attached to OnComplete messages when there is no // certificate error. TEST_F(URLLoaderTest, NoSSLInfoWithoutCertificateError) { @@ -2630,13 +3085,13 @@ class MockCookieObserver : public network::mojom::CookieAccessObserver { struct CookieDetails { CookieDetails(const mojom::CookieAccessDetailsPtr& details, - const net::CookieWithStatus cookie) + const net::CookieWithAccessResult cookie) : type(details->type), name(cookie.cookie.Name()), value(cookie.cookie.Value()), - is_include(cookie.status.IsInclude()), + is_include(cookie.access_result.status.IsInclude()), url(details->url), - status(cookie.status) {} + status(cookie.access_result.status) {} CookieDetails(CookieAccessType type, std::string name, @@ -2734,12 +3189,12 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { int32_t process_id, int32_t routing_id, const std::string& devtools_request_id, - const net::CookieAndLineStatusList& cookies_with_status, + const net::CookieAndLineAccessResultList& cookies_with_access_result, std::vector<network::mojom::HttpRawHeaderPairPtr> headers, const base::Optional<std::string>& raw_response_headers) override { raw_response_cookies_.insert(raw_response_cookies_.end(), - cookies_with_status.begin(), - cookies_with_status.end()); + cookies_with_access_result.begin(), + cookies_with_access_result.end()); devtools_request_id_ = devtools_request_id; @@ -2774,7 +3229,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { EXPECT_EQ(goal, raw_request_cookies_.size()); } - const net::CookieAndLineStatusList& raw_response_cookies() const { + const net::CookieAndLineAccessResultList& raw_response_cookies() const { return raw_response_cookies_; } @@ -2789,7 +3244,7 @@ class MockNetworkServiceClient : public TestNetworkServiceClient { } private: - net::CookieAndLineStatusList raw_response_cookies_; + net::CookieAndLineAccessResultList raw_response_cookies_; base::OnceClosure wait_for_raw_response_; size_t wait_for_raw_response_goal_ = 0u; std::string devtools_request_id_; @@ -3238,7 +3693,7 @@ TEST_F(URLLoaderTest, CorbExcludedWithNoCors) { std::unique_ptr<URLLoader> url_loader; mojom::URLLoaderFactoryParams params; params.process_id = 123; - CrossOriginReadBlocking::AddExceptionForPlugin(123); + CrossOriginReadBlockingExceptionForPlugin::AddExceptionForPlugin(123); url_loader = std::make_unique<URLLoader>( context(), nullptr /* network_service_client */, nullptr /* network_context_client */, @@ -3262,13 +3717,14 @@ TEST_F(URLLoaderTest, CorbExcludedWithNoCors) { // The request body is allowed through because CORB isn't applied. ASSERT_NE(std::string(), body); - CrossOriginReadBlocking::RemoveExceptionForPlugin(123); + CrossOriginReadBlockingExceptionForPlugin::RemoveExceptionForPlugin(123); } // This simulates a renderer that pretends to be proxying requests for Flash // (when browser didn't actually confirm that Flash is hosted by the given -// process via CrossOriginReadBlocking::AddExceptionForPlugin). We should still -// apply CORB in this case. +// process via +// CrossOriginReadBlockingExceptionForPlugin::AddExceptionForPlugin). We should +// still apply CORB in this case. TEST_F(URLLoaderTest, CorbEffectiveWithNoCorsWhenNoActualPlugin) { int kResourceType = 1; ResourceRequest request = @@ -3283,8 +3739,9 @@ TEST_F(URLLoaderTest, CorbEffectiveWithNoCorsWhenNoActualPlugin) { std::unique_ptr<URLLoader> url_loader; mojom::URLLoaderFactoryParams params; params.process_id = 234; - // No call to CrossOriginReadBlocking::AddExceptionForPlugin(123) - this is - // what we primarily want to cover in this test. + // No call to + // CrossOriginReadBlockingExceptionForPlugin::AddExceptionForPlugin(123) - + // this is what we primarily want to cover in this test. url_loader = std::make_unique<URLLoader>( context(), nullptr /* network_service_client */, nullptr /* network_context_client */, @@ -4338,8 +4795,8 @@ TEST_F(URLLoaderTest, RawResponseCookies) { network_service_client.raw_response_cookies()[0].cookie->Name()); EXPECT_EQ("b", network_service_client.raw_response_cookies()[0].cookie->Value()); - EXPECT_TRUE( - network_service_client.raw_response_cookies()[0].status.IsInclude()); + EXPECT_TRUE(network_service_client.raw_response_cookies()[0] + .access_result.status.IsInclude()); EXPECT_EQ("TEST", network_service_client.devtools_request_id()); @@ -4387,7 +4844,7 @@ TEST_F(URLLoaderTest, RawResponseCookiesInvalid) { EXPECT_FALSE(network_service_client.raw_response_cookies()[0].cookie); EXPECT_TRUE( network_service_client.raw_response_cookies()[0] - .status.HasExactlyExclusionReasonsForTesting( + .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_FAILURE_TO_STORE})); EXPECT_EQ("TEST", network_service_client.devtools_request_id()); @@ -4443,8 +4900,8 @@ TEST_F(URLLoaderTest, RawResponseCookiesRedirect) { network_service_client.raw_response_cookies()[0].cookie->Name()); EXPECT_EQ("true", network_service_client.raw_response_cookies()[0].cookie->Value()); - EXPECT_TRUE( - network_service_client.raw_response_cookies()[0].status.IsInclude()); + EXPECT_TRUE(network_service_client.raw_response_cookies()[0] + .access_result.status.IsInclude()); EXPECT_EQ("TEST", network_service_client.devtools_request_id()); } @@ -4491,7 +4948,7 @@ TEST_F(URLLoaderTest, RawResponseCookiesRedirect) { EXPECT_TRUE( network_service_client.raw_response_cookies()[0].cookie->IsSecure()); EXPECT_TRUE(network_service_client.raw_response_cookies()[0] - .status.HasExactlyExclusionReasonsForTesting( + .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); } } @@ -4538,8 +4995,8 @@ TEST_F(URLLoaderTest, RawResponseCookiesAuth) { network_service_client.raw_response_cookies()[0].cookie->Name()); EXPECT_EQ("true", network_service_client.raw_response_cookies()[0].cookie->Value()); - EXPECT_TRUE( - network_service_client.raw_response_cookies()[0].status.IsInclude()); + EXPECT_TRUE(network_service_client.raw_response_cookies()[0] + .access_result.status.IsInclude()); EXPECT_EQ("TEST", network_service_client.devtools_request_id()); } @@ -4584,7 +5041,7 @@ TEST_F(URLLoaderTest, RawResponseCookiesAuth) { EXPECT_TRUE( network_service_client.raw_response_cookies()[0].cookie->IsSecure()); EXPECT_TRUE(network_service_client.raw_response_cookies()[0] - .status.HasExactlyExclusionReasonsForTesting( + .access_result.status.HasExactlyExclusionReasonsForTesting( {net::CookieInclusionStatus::EXCLUDE_SECURE_ONLY})); EXPECT_EQ("TEST", network_service_client.devtools_request_id()); diff --git a/chromium/services/network/websocket.cc b/chromium/services/network/websocket.cc index dcf9d7ccb77..0b013c409b1 100644 --- a/chromium/services/network/websocket.cc +++ b/chromium/services/network/websocket.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/command_line.h" #include "base/feature_list.h" #include "base/location.h" #include "base/logging.h" @@ -185,7 +186,9 @@ void WebSocket::WebSocketEventHandler::OnAddChannelResponse( << " extensions=\"" << extensions << "\""; impl_->handshake_succeeded_ = true; - impl_->pending_connection_tracker_.OnCompleteHandshake(); + if (impl_->pending_connection_tracker_) { + impl_->pending_connection_tracker_->OnCompleteHandshake(); + } base::CommandLine* const command_line = base::CommandLine::ForCurrentProcess(); @@ -390,11 +393,13 @@ WebSocket::WebSocket( int32_t frame_id, const url::Origin& origin, uint32_t options, + net::NetworkTrafficAnnotationTag traffic_annotation, HasRawHeadersAccess has_raw_headers_access, mojo::PendingRemote<mojom::WebSocketHandshakeClient> handshake_client, mojo::PendingRemote<mojom::AuthenticationHandler> auth_handler, mojo::PendingRemote<mojom::TrustedHeaderClient> header_client, - WebSocketThrottler::PendingConnection pending_connection_tracker, + base::Optional<WebSocketThrottler::PendingConnection> + pending_connection_tracker, DataPipeUseTracker data_pipe_use_tracker, base::TimeDelta delay) : factory_(factory), @@ -404,6 +409,7 @@ WebSocket::WebSocket( pending_connection_tracker_(std::move(pending_connection_tracker)), delay_(delay), options_(options), + traffic_annotation_(traffic_annotation), child_id_(child_id), frame_id_(frame_id), origin_(std::move(origin)), @@ -423,6 +429,8 @@ WebSocket::WebSocket( // |isolation_info| must not be empty. DCHECK(!factory_->GetURLRequestContext()->require_network_isolation_key() || !isolation_info.IsEmpty()); + // |delay| should be zero if this connection is not throttled. + DCHECK(pending_connection_tracker.has_value() || delay.is_zero()); if (auth_handler_) { // Make sure the request dies if |auth_handler_| has an error, otherwise // requests can hang. @@ -603,7 +611,7 @@ void WebSocket::AddChannel( } channel_->SendAddChannelRequest(socket_url, requested_protocols, origin_, site_for_cookies, isolation_info, - headers_to_pass); + headers_to_pass, traffic_annotation_); } void WebSocket::OnWritable(MojoResult result, diff --git a/chromium/services/network/websocket.h b/chromium/services/network/websocket.h index 4d4f3ef8c08..b487133bd86 100644 --- a/chromium/services/network/websocket.h +++ b/chromium/services/network/websocket.h @@ -22,6 +22,7 @@ #include "base/util/type_safety/strong_alias.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" +#include "net/traffic_annotation/network_traffic_annotation.h" #include "net/websockets/websocket_event_interface.h" #include "services/network/network_service.h" #include "services/network/public/mojom/network_context.mojom.h" @@ -64,11 +65,13 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { int32_t render_frame_id, const url::Origin& origin, uint32_t options, + net::NetworkTrafficAnnotationTag traffic_annotation, HasRawHeadersAccess has_raw_cookie_access, mojo::PendingRemote<mojom::WebSocketHandshakeClient> handshake_client, mojo::PendingRemote<mojom::AuthenticationHandler> auth_handler, mojo::PendingRemote<mojom::TrustedHeaderClient> header_client, - WebSocketThrottler::PendingConnection pending_connection_tracker, + base::Optional<WebSocketThrottler::PendingConnection> + pending_connection_tracker, DataPipeUseTracker, base::TimeDelta delay); ~WebSocket() override; @@ -79,8 +82,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { void StartReceiving() override; void StartClosingHandshake(uint16_t code, const std::string& reason) override; - bool handshake_succeeded() const { return handshake_succeeded_; } - // Whether to allow sending/setting cookies during WebSocket handshakes for // |url|. This decision is based on the |options_| and |origin_| this // WebSocket was created with. @@ -183,7 +184,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { mojo::Remote<mojom::AuthenticationHandler> auth_handler_; mojo::Remote<mojom::TrustedHeaderClient> header_client_; - WebSocketThrottler::PendingConnection pending_connection_tracker_; + base::Optional<WebSocketThrottler::PendingConnection> + pending_connection_tracker_; // The channel we use to send events to the network. std::unique_ptr<net::WebSocketChannel> channel_; @@ -193,6 +195,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { const uint32_t options_; + const net::NetworkTrafficAnnotationTag traffic_annotation_; + const int32_t child_id_; const int32_t frame_id_; @@ -202,8 +206,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocket : public mojom::WebSocket { // For 3rd-party cookie permission checking. net::SiteForCookies site_for_cookies_; - // handshake_succeeded_ is used by WebSocketManager to manage counters for - // per-renderer WebSocket throttling. bool handshake_succeeded_ = false; const HasRawHeadersAccess has_raw_headers_access_; diff --git a/chromium/services/network/websocket_factory.cc b/chromium/services/network/websocket_factory.cc index 597b4bcfdc6..f5525ded93b 100644 --- a/chromium/services/network/websocket_factory.cc +++ b/chromium/services/network/websocket_factory.cc @@ -8,6 +8,7 @@ #include "mojo/public/cpp/bindings/message.h" #include "net/base/isolation_info.h" #include "net/base/url_util.h" +#include "net/traffic_annotation/network_traffic_annotation.h" #include "services/network/network_context.h" #include "services/network/network_service.h" #include "services/network/public/mojom/network_context.mojom.h" @@ -36,6 +37,7 @@ void WebSocketFactory::CreateWebSocket( int32_t render_frame_id, const url::Origin& origin, uint32_t options, + net::NetworkTrafficAnnotationTag traffic_annotation, mojo::PendingRemote<mojom::WebSocketHandshakeClient> handshake_client, mojo::PendingRemote<mojom::AuthenticationHandler> auth_handler, mojo::PendingRemote<mojom::TrustedHeaderClient> header_client) { @@ -61,8 +63,9 @@ void WebSocketFactory::CreateWebSocket( connections_.insert(std::make_unique<WebSocket>( this, url, requested_protocols, site_for_cookies, isolation_info, std::move(additional_headers), process_id, render_frame_id, origin, - options, has_raw_headers_access, std::move(handshake_client), - std::move(auth_handler), std::move(header_client), + options, traffic_annotation, has_raw_headers_access, + std::move(handshake_client), std::move(auth_handler), + std::move(header_client), throttler_.IssuePendingConnectionTracker(process_id), DataPipeUseTracker(context_->network_service(), DataPipeUser::kWebSocket), throttler_.CalculateDelay(process_id))); diff --git a/chromium/services/network/websocket_factory.h b/chromium/services/network/websocket_factory.h index 3a652f82515..87af5ae218c 100644 --- a/chromium/services/network/websocket_factory.h +++ b/chromium/services/network/websocket_factory.h @@ -22,6 +22,7 @@ namespace net { class IsolationInfo; class SiteForCookies; class SSLInfo; +struct NetworkTrafficAnnotationTag; } // namespace net namespace url { @@ -48,6 +49,7 @@ class WebSocketFactory final { int32_t render_frame_id, const url::Origin& origin, uint32_t options, + net::NetworkTrafficAnnotationTag traffic_annotation, mojo::PendingRemote<mojom::WebSocketHandshakeClient> handshake_client, mojo::PendingRemote<mojom::AuthenticationHandler> auth_handler, mojo::PendingRemote<mojom::TrustedHeaderClient> header_client); diff --git a/chromium/services/network/websocket_throttler.cc b/chromium/services/network/websocket_throttler.cc index d3809825556..8cc1ecfb6c9 100644 --- a/chromium/services/network/websocket_throttler.cc +++ b/chromium/services/network/websocket_throttler.cc @@ -7,6 +7,7 @@ #include <algorithm> #include "base/rand_util.h" +#include "services/network/public/mojom/network_context.mojom-forward.h" namespace network { @@ -93,8 +94,13 @@ base::TimeDelta WebSocketThrottler::CalculateDelay(int process_id) const { return it->second->CalculateDelay(); } -WebSocketThrottler::PendingConnection +base::Optional<WebSocketThrottler::PendingConnection> WebSocketThrottler::IssuePendingConnectionTracker(int process_id) { + if (process_id == mojom::kBrowserProcessId) { + // The browser process is not throttled. + return base::nullopt; + } + auto it = per_process_throttlers_.find(process_id); if (it == per_process_throttlers_.end()) { it = per_process_throttlers_ diff --git a/chromium/services/network/websocket_throttler.h b/chromium/services/network/websocket_throttler.h index 510c0575f4c..0226aacce0c 100644 --- a/chromium/services/network/websocket_throttler.h +++ b/chromium/services/network/websocket_throttler.h @@ -11,6 +11,7 @@ #include "base/component_export.h" #include "base/memory/weak_ptr.h" +#include "base/optional.h" #include "base/time/time.h" #include "base/timer/timer.h" @@ -103,8 +104,6 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocketPerProcessThrottler final { // This class is for throttling WebSocket connections. WebSocketThrottler is // a set of per-renderer throttlers. -// This class is only used in the network service. content::WebSocketManager -// uses WebSocketPerProcessThrottler directly. class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocketThrottler final { public: using PendingConnection = WebSocketPerProcessThrottler::PendingConnection; @@ -119,8 +118,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) WebSocketThrottler final { base::TimeDelta CalculateDelay(int process_id) const; // Returns a pending connection for |process_id|. This function can be called - // only when |HasTooManyPendingConnections(process_id)| is false. - PendingConnection IssuePendingConnectionTracker(int process_id); + // only when |HasTooManyPendingConnections(process_id)| is false. May return + // |base::nullopt| if |process_id| is not throttled. + base::Optional<PendingConnection> IssuePendingConnectionTracker( + int process_id); size_t GetSizeForTesting() const { return per_process_throttlers_.size(); } diff --git a/chromium/services/network/websocket_throttler_unittest.cc b/chromium/services/network/websocket_throttler_unittest.cc index 0d8b3326f8e..725a8e0b7e1 100644 --- a/chromium/services/network/websocket_throttler_unittest.cc +++ b/chromium/services/network/websocket_throttler_unittest.cc @@ -8,6 +8,7 @@ #include "base/optional.h" #include "base/test/task_environment.h" +#include "services/network/public/mojom/network_context.mojom-forward.h" #include "testing/gtest/include/gtest/gtest.h" namespace network { @@ -322,23 +323,35 @@ TEST_F(WebSocketThrottlerTest, TooManyPendingConnections) { for (int i = 0; i < limit - 1; ++i) { ASSERT_FALSE(throttler.HasTooManyPendingConnections(process1)); ASSERT_FALSE(throttler.HasTooManyPendingConnections(process2)); - trackers.push_back(throttler.IssuePendingConnectionTracker(process1)); - trackers.push_back(throttler.IssuePendingConnectionTracker(process2)); + trackers.push_back( + std::move(throttler.IssuePendingConnectionTracker(process1).value())); + trackers.push_back( + std::move(throttler.IssuePendingConnectionTracker(process2).value())); } EXPECT_EQ(2u, throttler.GetSizeForTesting()); ASSERT_FALSE(throttler.HasTooManyPendingConnections(process1)); ASSERT_FALSE(throttler.HasTooManyPendingConnections(process2)); - trackers.push_back(throttler.IssuePendingConnectionTracker(process1)); + trackers.push_back( + std::move(throttler.IssuePendingConnectionTracker(process1).value())); ASSERT_TRUE(throttler.HasTooManyPendingConnections(process1)); ASSERT_FALSE(throttler.HasTooManyPendingConnections(process2)); - trackers.push_back(throttler.IssuePendingConnectionTracker(process2)); + trackers.push_back( + std::move(throttler.IssuePendingConnectionTracker(process2).value())); ASSERT_TRUE(throttler.HasTooManyPendingConnections(process1)); ASSERT_TRUE(throttler.HasTooManyPendingConnections(process2)); } +TEST_F(WebSocketThrottlerTest, BrowserProcessNotThrottled) { + WebSocketThrottler throttler; + ASSERT_FALSE( + throttler.HasTooManyPendingConnections(mojom::kBrowserProcessId)); + ASSERT_FALSE(throttler.IssuePendingConnectionTracker(mojom::kBrowserProcessId) + .has_value()); +} + } // namespace } // namespace network diff --git a/chromium/services/preferences/tracked/BUILD.gn b/chromium/services/preferences/tracked/BUILD.gn index 6802e27ffdd..ccd24350291 100644 --- a/chromium/services/preferences/tracked/BUILD.gn +++ b/chromium/services/preferences/tracked/BUILD.gn @@ -59,7 +59,7 @@ source_set("tracked") { ] if (is_mac) { - libs = [ "IOKit.framework" ] + frameworks = [ "IOKit.framework" ] } } diff --git a/chromium/services/preferences/tracked/device_id_unittest.cc b/chromium/services/preferences/tracked/device_id_unittest.cc index 297cf42e618..11c16b44ab6 100644 --- a/chromium/services/preferences/tracked/device_id_unittest.cc +++ b/chromium/services/preferences/tracked/device_id_unittest.cc @@ -12,7 +12,7 @@ TEST(GetDeterministicMachineSpecificIdTest, IsDeterministic) { std::string second_machine_id; const MachineIdStatus kExpectedStatus = -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) +#if defined(OS_WIN) || defined(OS_MAC) MachineIdStatus::SUCCESS; #else MachineIdStatus::NOT_IMPLEMENTED; diff --git a/chromium/services/proxy_resolver/public/cpp/OWNERS b/chromium/services/proxy_resolver/public/cpp/OWNERS index 59d61d1933d..d5fefd82012 100644 --- a/chromium/services/proxy_resolver/public/cpp/OWNERS +++ b/chromium/services/proxy_resolver/public/cpp/OWNERS @@ -1,8 +1,2 @@ per-file *_mojom_traits*.*=set noparent per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS -per-file manifest.cc=set noparent -per-file manifest.cc=file://ipc/SECURITY_OWNERS -per-file manifest.h=set noparent -per-file manifest.h=file://ipc/SECURITY_OWNERS diff --git a/chromium/services/proxy_resolver/public/cpp/mojo_host_mojom_traits.h b/chromium/services/proxy_resolver/public/cpp/mojo_host_mojom_traits.h index 5dfd827b4ab..8ca2bbfc8ea 100644 --- a/chromium/services/proxy_resolver/public/cpp/mojo_host_mojom_traits.h +++ b/chromium/services/proxy_resolver/public/cpp/mojo_host_mojom_traits.h @@ -8,7 +8,7 @@ #include "base/strings/string_piece.h" #include "mojo/public/cpp/bindings/enum_traits.h" #include "net/proxy_resolution/proxy_resolve_dns_operation.h" -#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h" +#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom-shared.h" namespace mojo { diff --git a/chromium/services/proxy_resolver/public/cpp/proxy_resolver.typemap b/chromium/services/proxy_resolver/public/cpp/proxy_resolver.typemap deleted file mode 100644 index db1f4b0ea80..00000000000 --- a/chromium/services/proxy_resolver/public/cpp/proxy_resolver.typemap +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//services/proxy_resolver/public/mojom/proxy_resolver.mojom" -public_headers = [ - "//net/base/proxy_server.h", - "//net/proxy_resolution/proxy_info.h", - "//net/proxy_resolution/proxy_resolve_dns_operation.h", -] -traits_headers = [ - "//services/proxy_resolver/public/cpp/mojo_host_mojom_traits.h", - "//services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits.h", -] -sources = [ - "//services/proxy_resolver/public/cpp/mojo_host_mojom_traits.cc", - "//services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits.cc", -] -type_mappings = [ - "proxy_resolver.mojom.HostResolveOperation=::net::ProxyResolveDnsOperation", - "proxy_resolver.mojom.ProxyInfo=::net::ProxyInfo", - "proxy_resolver.mojom.ProxyServer=::net::ProxyServer", - "proxy_resolver.mojom.ProxyServer::Scheme=::net::ProxyScheme", -] -public_deps = [ - "//net", -] diff --git a/chromium/services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits.h b/chromium/services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits.h index 4c434c4b417..32ab0c7709d 100644 --- a/chromium/services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits.h +++ b/chromium/services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits.h @@ -12,7 +12,7 @@ #include "net/base/proxy_server.h" #include "net/proxy_resolution/proxy_info.h" #include "net/proxy_resolution/proxy_list.h" -#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h" +#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom-shared.h" namespace net { class ProxyInfo; diff --git a/chromium/services/proxy_resolver/public/cpp/typemaps.gni b/chromium/services/proxy_resolver/public/cpp/typemaps.gni deleted file mode 100644 index babbad449ce..00000000000 --- a/chromium/services/proxy_resolver/public/cpp/typemaps.gni +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -typemaps = [ "//services/proxy_resolver/public/cpp/proxy_resolver.typemap" ] diff --git a/chromium/services/proxy_resolver/public/mojom/BUILD.gn b/chromium/services/proxy_resolver/public/mojom/BUILD.gn index d82c4009965..77790f8e173 100644 --- a/chromium/services/proxy_resolver/public/mojom/BUILD.gn +++ b/chromium/services/proxy_resolver/public/mojom/BUILD.gn @@ -18,4 +18,38 @@ mojom("mojom") { export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1" export_header_blink = "third_party/blink/public/platform/web_common.h" } + + cpp_typemaps = [ + { + types = [ + { + mojom = "proxy_resolver.mojom.HostResolveOperation" + cpp = "::net::ProxyResolveDnsOperation" + }, + + { + mojom = "proxy_resolver.mojom.ProxyInfo" + cpp = "::net::ProxyInfo" + }, + + { + mojom = "proxy_resolver.mojom.ProxyServer" + cpp = "::net::ProxyServer" + }, + + { + mojom = "proxy_resolver.mojom.ProxyServer::Scheme" + cpp = "::net::ProxyScheme" + }, + ] + traits_headers = [ + "//services/proxy_resolver/public/cpp/mojo_host_mojom_traits.h", + "//services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits.h", + ] + traits_sources = [ + "//services/proxy_resolver/public/cpp/mojo_host_mojom_traits.cc", + "//services/proxy_resolver/public/cpp/proxy_resolver_mojom_traits.cc", + ] + }, + ] } diff --git a/chromium/services/resource_coordinator/BUILD.gn b/chromium/services/resource_coordinator/BUILD.gn index cc38677069d..fb47a3d1911 100644 --- a/chromium/services/resource_coordinator/BUILD.gn +++ b/chromium/services/resource_coordinator/BUILD.gn @@ -15,6 +15,8 @@ source_set("lib") { "memory_instrumentation/graph.h", "memory_instrumentation/graph_processor.cc", "memory_instrumentation/graph_processor.h", + "memory_instrumentation/memory_dump_map_converter.cc", + "memory_instrumentation/memory_dump_map_converter.h", "memory_instrumentation/queued_request.cc", "memory_instrumentation/queued_request.h", "memory_instrumentation/queued_request_dispatcher.cc", @@ -44,6 +46,7 @@ source_set("tests") { "memory_instrumentation/coordinator_impl_unittest.cc", "memory_instrumentation/graph_processor_unittest.cc", "memory_instrumentation/graph_unittest.cc", + "memory_instrumentation/memory_dump_map_converter_unittest.cc", "public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits_unittest.cc", "public/cpp/memory_instrumentation/os_metrics_unittest.cc", "public/cpp/memory_instrumentation/tracing_integration_unittest.cc", diff --git a/chromium/services/resource_coordinator/DEPS b/chromium/services/resource_coordinator/DEPS index 9765ce49d0f..7e714d03182 100644 --- a/chromium/services/resource_coordinator/DEPS +++ b/chromium/services/resource_coordinator/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+components/ukm/test_ukm_recorder.h", + "+third_party/perfetto", "+third_party/smhasher", "+services/metrics/public", ] diff --git a/chromium/services/resource_coordinator/memory_instrumentation/OWNERS b/chromium/services/resource_coordinator/memory_instrumentation/OWNERS index 62d73f2aea3..e13658dd0d4 100644 --- a/chromium/services/resource_coordinator/memory_instrumentation/OWNERS +++ b/chromium/services/resource_coordinator/memory_instrumentation/OWNERS @@ -1,3 +1,4 @@ hjd@chromium.org primiano@chromium.org +ssid@chromium.org # COMPONENT: Internals>Instrumentation>Memory diff --git a/chromium/services/resource_coordinator/memory_instrumentation/coordinator_impl.cc b/chromium/services/resource_coordinator/memory_instrumentation/coordinator_impl.cc index cbfa18439ce..37facb9f51b 100644 --- a/chromium/services/resource_coordinator/memory_instrumentation/coordinator_impl.cc +++ b/chromium/services/resource_coordinator/memory_instrumentation/coordinator_impl.cc @@ -29,7 +29,7 @@ #include "services/resource_coordinator/public/mojom/memory_instrumentation/constants.mojom.h" #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h" -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MAC) #include "base/mac/mac_util.h" #endif diff --git a/chromium/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc b/chromium/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc index 00ba4480852..ae218fdf6cf 100644 --- a/chromium/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc +++ b/chromium/services/resource_coordinator/memory_instrumentation/coordinator_impl_unittest.cc @@ -14,7 +14,6 @@ #include "base/time/time.h" #include "base/trace_event/memory_dump_request_args.h" #include "build/build_config.h" -#include "mojo/public/cpp/bindings/interface_request.h" #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -443,7 +442,7 @@ TEST_F(CoordinatorImplTest, TimeOutStuckChildMultiProcess) { // This ifdef is here to match the sandboxing behavior of the client. // On Linux, all memory dumps come from the browser client. On all other // platforms, they are expected to come from each individual client. -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) EXPECT_CALL(browser_client, RequestOSMemoryDumpMock( _, AllOf(Contains(kBrowserPid), Contains(kRendererPid)), _)) @@ -473,7 +472,7 @@ TEST_F(CoordinatorImplTest, TimeOutStuckChildMultiProcess) { results[0] = FillRawOSDump(kRendererPid); std::move(callback).Run(true, std::move(results)); })); -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) // Make the browser respond correctly but pretend the renderer is "stuck" // by storing a callback. @@ -620,7 +619,7 @@ TEST_F(CoordinatorImplTest, GlobalMemoryDumpStruct) { MemoryAllocatorDump::kUnitsBytes, 1024 * 2); std::move(callback).Run(true, args.dump_guid, std::move(pmd)); })); -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) EXPECT_CALL(browser_client, RequestOSMemoryDumpMock(_, AllOf(Contains(1), Contains(2)), _)) .WillOnce(Invoke( @@ -661,7 +660,7 @@ TEST_F(CoordinatorImplTest, GlobalMemoryDumpStruct) { results[0]->resident_set_kb = 2; std::move(callback).Run(true, std::move(results)); })); -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) MockGlobalMemoryDumpCallback callback; EXPECT_CALL(callback, OnCall(true, NotNull())) @@ -705,7 +704,7 @@ TEST_F(CoordinatorImplTest, VmRegionsForHeapProfiler) { // This ifdef is here to match the sandboxing behavior of the client. // On Linux, all memory dumps come from the browser client. On all other // platforms, they are expected to come from each individual client. -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) EXPECT_CALL(browser_client, RequestOSMemoryDumpMock( _, AllOf(Contains(kBrowserPid), Contains(kRendererPid)), _)) @@ -735,7 +734,7 @@ TEST_F(CoordinatorImplTest, VmRegionsForHeapProfiler) { results[0] = FillRawOSDump(kRendererPid); std::move(callback).Run(true, std::move(results)); })); -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) MockGetVmRegionsForHeapProfilerCallback callback; EXPECT_CALL(callback, OnCall(_)) @@ -867,7 +866,7 @@ TEST_F(CoordinatorImplTest, DumpByPidSuccess) { // This ifdef is here to match the sandboxing behavior of the client. // On Linux, all memory dumps come from the browser client. On all other // platforms, they are expected to come from each individual client. -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) EXPECT_CALL(client_process_1, RequestOSMemoryDumpMock(_, _, _)) .WillOnce(Invoke( [](mojom::MemoryMapOption, const std::vector<base::ProcessId>& pids, @@ -915,7 +914,7 @@ TEST_F(CoordinatorImplTest, DumpByPidSuccess) { results[0] = FillRawOSDump(kGpuPid); std::move(callback).Run(true, std::move(results)); })); -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) base::RunLoop run_loop; diff --git a/chromium/services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.cc b/chromium/services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.cc new file mode 100644 index 00000000000..e6c09f7a476 --- /dev/null +++ b/chromium/services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.cc @@ -0,0 +1,151 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.h" + +#include <list> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/trace_event/process_memory_dump.h" +#include "third_party/perfetto/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h" + +namespace memory_instrumentation { + +MemoryDumpMapConverter::MemoryDumpMapConverter() = default; +MemoryDumpMapConverter::~MemoryDumpMapConverter() = default; + +perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap +MemoryDumpMapConverter::Convert(const MemoryDumpMap& input) { + perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap output; + + for (const auto& entry : input) { + const base::ProcessId process_id = entry.first; + const base::trace_event::ProcessMemoryDump* process_dump = entry.second; + + if (process_dump == nullptr) + continue; + + output.emplace(process_id, ConvertProcessMemoryDump(*process_dump)); + } + + return output; +} + +std::unique_ptr<MemoryDumpMapConverter::PerfettoProcessMemoryNode> +MemoryDumpMapConverter::ConvertProcessMemoryDump( + const base::trace_event::ProcessMemoryDump& input) { + const perfetto::trace_processor::LevelOfDetail level_of_detail = + ConvertLevelOfDetail(input.dump_args().level_of_detail); + + MemoryDumpMapConverter::PerfettoProcessMemoryNode::MemoryNodesMap nodes_map = + ConvertAllocatorDumps(input); + MemoryDumpMapConverter::PerfettoProcessMemoryNode::AllocatorNodeEdgesMap + edges_map = ConvertAllocatorDumpEdges(input); + + return std::make_unique<PerfettoProcessMemoryNode>( + level_of_detail, std::move(edges_map), std::move(nodes_map)); +} + +perfetto::trace_processor::LevelOfDetail +MemoryDumpMapConverter::ConvertLevelOfDetail( + const base::trace_event::MemoryDumpLevelOfDetail& input) const { + switch (input) { + case base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND: + return perfetto::trace_processor::LevelOfDetail::kBackground; + case base::trace_event::MemoryDumpLevelOfDetail::LIGHT: + return perfetto::trace_processor::LevelOfDetail::kLight; + case base::trace_event::MemoryDumpLevelOfDetail::DETAILED: + return perfetto::trace_processor::LevelOfDetail::kDetailed; + } + return perfetto::trace_processor::LevelOfDetail::kDetailed; +} + +std::vector<perfetto::trace_processor::RawMemoryGraphNode::MemoryNodeEntry> +MemoryDumpMapConverter::ConvertAllocatorDumpEntries( + const base::trace_event::MemoryAllocatorDump& input) const { + std::vector<perfetto::trace_processor::RawMemoryGraphNode::MemoryNodeEntry> + output; + + for (const auto& entry : input.entries()) { + if (entry.entry_type == + base::trace_event::MemoryAllocatorDump::Entry::kUint64) { + output.emplace_back(entry.name, entry.units, entry.value_uint64); + } else { + output.emplace_back(entry.name, entry.units, entry.value_string); + } + } + return output; +} + +std::unique_ptr<perfetto::trace_processor::RawMemoryGraphNode> +MemoryDumpMapConverter::ConvertMemoryAllocatorDump( + const base::trace_event::MemoryAllocatorDump& input) const { + auto output = std::make_unique<perfetto::trace_processor::RawMemoryGraphNode>( + input.absolute_name(), ConvertLevelOfDetail(input.level_of_detail()), + ConvertMemoryAllocatorDumpGuid(input.guid()), + ConvertAllocatorDumpEntries(input)); + + CopyAndConvertAllocatorDumpFlags(input, output.get()); + + return output; +} + +void MemoryDumpMapConverter::CopyAndConvertAllocatorDumpFlags( + const base::trace_event::MemoryAllocatorDump& input, + perfetto::trace_processor::RawMemoryGraphNode* output) const { + output->clear_flags(output->flags()); + output->set_flags( + input.flags() & base::trace_event::MemoryAllocatorDump::WEAK + ? perfetto::trace_processor::RawMemoryGraphNode::kWeak + : perfetto::trace_processor::RawMemoryGraphNode::kDefault); +} + +MemoryDumpMapConverter::PerfettoProcessMemoryNode::MemoryNodesMap +MemoryDumpMapConverter::ConvertAllocatorDumps( + const base::trace_event::ProcessMemoryDump& input) const { + MemoryDumpMapConverter::PerfettoProcessMemoryNode::MemoryNodesMap output; + for (const auto& entry : input.allocator_dumps()) { + const std::unique_ptr<base::trace_event::MemoryAllocatorDump>& dump = + entry.second; + + output.emplace(entry.first, ConvertMemoryAllocatorDump(*dump)); + } + return output; +} + +MemoryDumpMapConverter::PerfettoProcessMemoryNode::AllocatorNodeEdgesMap +MemoryDumpMapConverter::ConvertAllocatorDumpEdges( + const base::trace_event::ProcessMemoryDump& input) const { + MemoryDumpMapConverter::PerfettoProcessMemoryNode::AllocatorNodeEdgesMap + output; + for (const auto& entry : input.allocator_dumps_edges()) { + std::unique_ptr<perfetto::trace_processor::MemoryGraphEdge> edge = + ConvertAllocatorDumpEdge(entry.second); + const perfetto::trace_processor::MemoryAllocatorNodeId source = + edge->source; + output.emplace(source, std::move(edge)); + } + return output; +} + +std::unique_ptr<perfetto::trace_processor::MemoryGraphEdge> +MemoryDumpMapConverter::ConvertAllocatorDumpEdge( + const base::trace_event::ProcessMemoryDump::MemoryAllocatorDumpEdge& input) + const { + return std::make_unique<perfetto::trace_processor::MemoryGraphEdge>( + ConvertMemoryAllocatorDumpGuid(input.source), + ConvertMemoryAllocatorDumpGuid(input.target), input.importance, + input.overridable); +} + +perfetto::trace_processor::MemoryAllocatorNodeId +MemoryDumpMapConverter::ConvertMemoryAllocatorDumpGuid( + const base::trace_event::MemoryAllocatorDumpGuid& input) const { + return perfetto::trace_processor::MemoryAllocatorNodeId(input.ToUint64()); +} + +} // namespace memory_instrumentation diff --git a/chromium/services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.h b/chromium/services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.h new file mode 100644 index 00000000000..f01fa30f32f --- /dev/null +++ b/chromium/services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.h @@ -0,0 +1,83 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_MEMORY_DUMP_MAP_CONVERTER_H_ +#define SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_MEMORY_DUMP_MAP_CONVERTER_H_ + +#include <list> +#include <map> +#include <memory> +#include <vector> + +#include "base/trace_event/process_memory_dump.h" +#include "third_party/perfetto/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h" + +namespace memory_instrumentation { + +// Converts the Chromium MemoryDumpMap to the corresponding defined in Perfetto +// type RawMemoryNodeMap. +// +// Example usage: +// +// { +// base::trace_event::ProcessMemoryDump pmd; +// +// MemoryDumpMapConverter converter; +// perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap +// perfettoNodeMap = converter.Convert(pmd); +// } +class MemoryDumpMapConverter { + public: + using MemoryDumpMap = + std::map<base::ProcessId, const base::trace_event::ProcessMemoryDump*>; + + using PerfettoProcessMemoryNode = + perfetto::trace_processor::RawProcessMemoryNode; + + MemoryDumpMapConverter(); + ~MemoryDumpMapConverter(); + MemoryDumpMapConverter(const MemoryDumpMapConverter&) = delete; + void operator=(const MemoryDumpMapConverter&) = delete; + + perfetto::trace_processor::GraphProcessor::RawMemoryNodeMap Convert( + const MemoryDumpMap& input); + + private: + std::unique_ptr<MemoryDumpMapConverter::PerfettoProcessMemoryNode> + ConvertProcessMemoryDump(const base::trace_event::ProcessMemoryDump& input); + + perfetto::trace_processor::LevelOfDetail ConvertLevelOfDetail( + const base::trace_event::MemoryDumpLevelOfDetail& input) const; + + PerfettoProcessMemoryNode::MemoryNodesMap ConvertAllocatorDumps( + const base::trace_event::ProcessMemoryDump& input) const; + + std::unique_ptr<perfetto::trace_processor::RawMemoryGraphNode> + ConvertMemoryAllocatorDump( + const base::trace_event::MemoryAllocatorDump& input) const; + + void CopyAndConvertAllocatorDumpFlags( + const base::trace_event::MemoryAllocatorDump& input, + perfetto::trace_processor::RawMemoryGraphNode* output) const; + + std::vector<perfetto::trace_processor::RawMemoryGraphNode::MemoryNodeEntry> + ConvertAllocatorDumpEntries( + const base::trace_event::MemoryAllocatorDump& input) const; + + PerfettoProcessMemoryNode::AllocatorNodeEdgesMap ConvertAllocatorDumpEdges( + const base::trace_event::ProcessMemoryDump& input) const; + + std::unique_ptr<perfetto::trace_processor::MemoryGraphEdge> + ConvertAllocatorDumpEdge( + const base::trace_event::ProcessMemoryDump::MemoryAllocatorDumpEdge& + input) const; + + perfetto::trace_processor::MemoryAllocatorNodeId + ConvertMemoryAllocatorDumpGuid( + const base::trace_event::MemoryAllocatorDumpGuid& input) const; +}; + +} // namespace memory_instrumentation + +#endif // SERVICES_RESOURCE_COORDINATOR_MEMORY_INSTRUMENTATION_MEMORY_DUMP_MAP_CONVERTER_H_ diff --git a/chromium/services/resource_coordinator/memory_instrumentation/memory_dump_map_converter_unittest.cc b/chromium/services/resource_coordinator/memory_instrumentation/memory_dump_map_converter_unittest.cc new file mode 100644 index 00000000000..bce58c22181 --- /dev/null +++ b/chromium/services/resource_coordinator/memory_instrumentation/memory_dump_map_converter_unittest.cc @@ -0,0 +1,86 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/resource_coordinator/memory_instrumentation/memory_dump_map_converter.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace memory_instrumentation { + +using base::trace_event::MemoryAllocatorDump; +using base::trace_event::MemoryAllocatorDumpGuid; +using base::trace_event::MemoryDumpArgs; +using base::trace_event::MemoryDumpLevelOfDetail; +using base::trace_event::ProcessMemoryDump; + +TEST(MemoryDumpMapConverter, Convert) { + MemoryDumpMapConverter::MemoryDumpMap process_dumps; + + MemoryDumpArgs dump_args1 = {MemoryDumpLevelOfDetail::DETAILED}; + ProcessMemoryDump pmd1(dump_args1); + + auto* source1 = pmd1.CreateAllocatorDump("test1/test2/test3"); + source1->AddScalar(MemoryAllocatorDump::kNameSize, + MemoryAllocatorDump::kUnitsBytes, 10); + + auto* target1 = pmd1.CreateAllocatorDump("target1"); + pmd1.AddOwnershipEdge(source1->guid(), target1->guid(), 10); + + pmd1.CreateWeakSharedGlobalAllocatorDump(MemoryAllocatorDumpGuid(1)); + + process_dumps.emplace(1, &pmd1); + + MemoryDumpArgs dump_args2 = {MemoryDumpLevelOfDetail::LIGHT}; + ProcessMemoryDump pmd2(dump_args2); + + auto* source2 = pmd2.CreateAllocatorDump("test1/test4/test5"); + source2->AddScalar(MemoryAllocatorDump::kNameSize, + MemoryAllocatorDump::kUnitsObjects, 1); + + process_dumps.emplace(2, &pmd2); + + MemoryDumpMapConverter converter; + auto output_dump_map = converter.Convert(process_dumps); + + ASSERT_EQ(output_dump_map.size(), 2uL); + ASSERT_NE(output_dump_map.find(1), output_dump_map.end()); + ASSERT_EQ(output_dump_map[1]->allocator_nodes().size(), 3uL); + ASSERT_NE(output_dump_map[1]->allocator_nodes().find("test1/test2/test3"), + output_dump_map[1]->allocator_nodes().end()); + { + const auto& dump = + *output_dump_map[1]->allocator_nodes().at("test1/test2/test3"); + + ASSERT_EQ(dump.entries().size(), 1uL); + const auto& entry = dump.entries()[0]; + ASSERT_EQ(entry.entry_type, perfetto::trace_processor::RawMemoryGraphNode:: + MemoryNodeEntry::kUint64); + ASSERT_EQ(entry.units, "bytes"); + ASSERT_EQ(entry.value_uint64, 10uL); + } + + ASSERT_NE(output_dump_map[1]->allocator_nodes().find("target1"), + output_dump_map[1]->allocator_nodes().end()); + ASSERT_NE(output_dump_map[1]->allocator_nodes().find("global/1"), + output_dump_map[1]->allocator_nodes().end()); + + ASSERT_NE(output_dump_map.find(2), output_dump_map.end()); + ASSERT_EQ(output_dump_map[2]->allocator_nodes().size(), 1uL); + ASSERT_NE(output_dump_map[2]->allocator_nodes().find("test1/test4/test5"), + output_dump_map[2]->allocator_nodes().end()); + + { + const auto& dump = + *output_dump_map[2]->allocator_nodes().at("test1/test4/test5"); + + ASSERT_EQ(dump.entries().size(), 1uL); + const auto& entry = dump.entries()[0]; + ASSERT_EQ(entry.entry_type, perfetto::trace_processor::RawMemoryGraphNode:: + MemoryNodeEntry::kUint64); + ASSERT_EQ(entry.units, "objects"); + ASSERT_EQ(entry.value_uint64, 1uL); + } +} + +} // namespace memory_instrumentation diff --git a/chromium/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc b/chromium/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc index 632e8da62e0..9810dcb1fb2 100644 --- a/chromium/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc +++ b/chromium/services/resource_coordinator/memory_instrumentation/queued_request_dispatcher.cc @@ -21,7 +21,7 @@ #include "services/resource_coordinator/memory_instrumentation/switches.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h" -#if defined(OS_MACOSX) && !defined(OS_IOS) +#if defined(OS_MAC) #include "base/mac/mac_util.h" #endif @@ -43,11 +43,11 @@ namespace { uint32_t CalculatePrivateFootprintKb(const mojom::RawOSMemDump& os_dump, uint32_t shared_resident_kb) { DCHECK(os_dump.platform_private_footprint); -#if defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) uint64_t rss_anon_bytes = os_dump.platform_private_footprint->rss_anon_bytes; uint64_t vm_swap_bytes = os_dump.platform_private_footprint->vm_swap_bytes; return (rss_anon_bytes + vm_swap_bytes) / 1024; -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) if (base::mac::IsAtLeastOS10_12()) { uint64_t phys_footprint_bytes = os_dump.platform_private_footprint->phys_footprint_bytes; @@ -82,7 +82,7 @@ memory_instrumentation::mojom::OSMemDumpPtr CreatePublicOSDump( os_dump->is_peak_rss_resettable = internal_os_dump.is_peak_rss_resettable; os_dump->private_footprint_kb = CalculatePrivateFootprintKb(internal_os_dump, shared_resident_kb); -#if defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) os_dump->private_footprint_swap_kb = internal_os_dump.platform_private_footprint->vm_swap_bytes / 1024; #endif @@ -243,12 +243,12 @@ void QueuedRequestDispatcher::SetUpAndDispatch( // On most platforms each process can dump data about their own process // so ask each process to do so Linux is special see below. -#if !defined(OS_LINUX) +#if !defined(OS_LINUX) && !defined(OS_CHROMEOS) request->pending_responses.insert({client_info.pid, ResponseType::kOSDump}); client->RequestOSMemoryDump(request->memory_map_option(), {base::kNullProcessId}, base::BindOnce(os_callback, client_info.pid)); -#endif // !defined(OS_LINUX) +#endif // !defined(OS_LINUX) && !defined(OS_CHROMEOS) // If we are in the single pid case, then we've already found the only // process we're looking for. @@ -258,7 +258,7 @@ void QueuedRequestDispatcher::SetUpAndDispatch( // In some cases, OS stats can only be dumped from a privileged process to // get around to sandboxing/selinux restrictions (see crbug.com/461788). -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) std::vector<base::ProcessId> pids; mojom::ClientProcess* browser_client = nullptr; base::ProcessId browser_client_pid = base::kNullProcessId; @@ -283,7 +283,7 @@ void QueuedRequestDispatcher::SetUpAndDispatch( browser_client->RequestOSMemoryDump(request->memory_map_option(), pids, std::move(callback)); } -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) // In this case, we have not found the pid we are looking for so increment // the failed dump count and exit. @@ -302,7 +302,7 @@ void QueuedRequestDispatcher::SetUpAndDispatchVmRegionRequest( const OsCallback& os_callback) { // On Linux, OS stats can only be dumped from a privileged process to // get around to sandboxing/selinux restrictions (see crbug.com/461788). -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) mojom::ClientProcess* browser_client = nullptr; base::ProcessId browser_client_pid = 0; for (const auto& client_info : clients) { @@ -337,7 +337,7 @@ void QueuedRequestDispatcher::SetUpAndDispatchVmRegionRequest( base::BindOnce(os_callback, client_info.pid)); } } -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) } // static @@ -353,7 +353,7 @@ QueuedRequestDispatcher::FinalizeVmRegionRequest( // each client process provides 1 OS dump, % the case where the client is // disconnected mid dump. OSMemDumpMap& extra_os_dumps = response.second.os_dumps; -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) for (auto& kv : extra_os_dumps) { auto pid = kv.first == base::kNullProcessId ? original_pid : kv.first; DCHECK(results.find(pid) == results.end()); @@ -414,7 +414,7 @@ void QueuedRequestDispatcher::Finalize(QueuedRequest* request, // crash). In the latter case (OS_LINUX) we expect the full map to come // from the browser process response. OSMemDumpMap& extra_os_dumps = response.second.os_dumps; -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) for (const auto& kv : extra_os_dumps) { auto pid = kv.first == base::kNullProcessId ? original_pid : kv.first; DCHECK_EQ(pid_to_os_dump[pid], nullptr); @@ -475,7 +475,7 @@ void QueuedRequestDispatcher::Finalize(QueuedRequest* request, mojom::OSMemDumpPtr os_dump = nullptr; if (raw_os_dump) { uint64_t shared_resident_kb = 0; -#if defined(OS_MACOSX) +#if defined(OS_MAC) // The resident, anonymous shared memory for each process is only relevant // on macOS. const auto process_graph_it = diff --git a/chromium/services/resource_coordinator/public/cpp/OWNERS b/chromium/services/resource_coordinator/public/cpp/OWNERS index a605b82578c..4e0f5fdc988 100644 --- a/chromium/services/resource_coordinator/public/cpp/OWNERS +++ b/chromium/services/resource_coordinator/public/cpp/OWNERS @@ -1,7 +1 @@ per-file BUILD.gn=file://services/resource_coordinator/memory_instrumentation/OWNERS - -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS - -per-file *_mojom_traits*.*=set noparent -per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/OWNERS b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/OWNERS index a5521d03293..887892c3ef3 100644 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/OWNERS +++ b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/OWNERS @@ -1,7 +1,4 @@ file://services/resource_coordinator/memory_instrumentation/OWNERS -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS - per-file *_mojom_traits*.*=set noparent per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc index b39166af6b2..b96cb6d7215 100644 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc +++ b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.cc @@ -12,7 +12,6 @@ #include "base/synchronization/lock.h" #include "base/trace_event/memory_dump_request_args.h" #include "build/build_config.h" -#include "mojo/public/cpp/bindings/interface_request.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.h" @@ -141,7 +140,7 @@ void ClientProcessImpl::RequestOSMemoryDump( args.pids = pids; args.callback = std::move(callback); -#if defined(OS_MACOSX) +#if defined(OS_MAC) // If the most recent chrome memory dump hasn't finished, wait for that to // finish. if (most_recent_chrome_memory_dump_guid_.has_value()) { diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.typemap b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.typemap deleted file mode 100644 index 421b6ee7f0b..00000000000 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.typemap +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom" - -public_headers = [ - "//base/trace_event/memory_dump_request_args.h", - "//base/trace_event/process_memory_dump.h", - "//base/trace_event/memory_allocator_dump.h", - "//base/trace_event/memory_dump_manager.h", -] -traits_headers = [ "//services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.h" ] -sources = [ - "//services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.cc", - "//services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.h", -] -deps = [ - "//base", -] -type_mappings = [ - "memory_instrumentation.mojom.DumpType=::base::trace_event::MemoryDumpType", - "memory_instrumentation.mojom.LevelOfDetail=::base::trace_event::MemoryDumpLevelOfDetail", - "memory_instrumentation.mojom.Determinism=::base::trace_event::MemoryDumpDeterminism", - "memory_instrumentation.mojom.RequestArgs=::base::trace_event::MemoryDumpRequestArgs", - "memory_instrumentation.mojom.RawAllocatorDumpEdge=::base::trace_event::ProcessMemoryDump::MemoryAllocatorDumpEdge", - "memory_instrumentation.mojom.RawAllocatorDumpEntry=::base::trace_event::MemoryAllocatorDump::Entry[move_only]", - "memory_instrumentation.mojom.RawAllocatorDump=::std::unique_ptr<::base::trace_event::MemoryAllocatorDump>[move_only]", - "memory_instrumentation.mojom.RawProcessMemoryDump=::std::unique_ptr<::base::trace_event::ProcessMemoryDump>[move_only,nullable_is_same_type]", -] diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.cc b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.cc index a7ab6f897af..56040f9e835 100644 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.cc +++ b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.cc @@ -4,6 +4,8 @@ #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.h" +#include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h" + namespace mojo { // static diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.h b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.h index 6493feeb02d..44829fe9e12 100644 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.h +++ b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.h @@ -7,9 +7,11 @@ #include "base/component_export.h" #include "base/process/process_handle.h" +#include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/memory_dump_request_args.h" -#include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h" +#include "base/trace_event/process_memory_dump.h" +#include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom-shared.h" namespace mojo { @@ -100,19 +102,19 @@ template <> struct COMPONENT_EXPORT(RESOURCE_COORDINATOR_PUBLIC_MOJOM) UnionTraits< memory_instrumentation::mojom::RawAllocatorDumpEntryValueDataView, base::trace_event::MemoryAllocatorDump::Entry> { - static memory_instrumentation::mojom::RawAllocatorDumpEntryValue::Tag GetTag( - const base::trace_event::MemoryAllocatorDump::Entry& args) { + static memory_instrumentation::mojom::RawAllocatorDumpEntryValueDataView::Tag + GetTag(const base::trace_event::MemoryAllocatorDump::Entry& args) { switch (args.entry_type) { case base::trace_event::MemoryAllocatorDump::Entry::EntryType::kUint64: - return memory_instrumentation::mojom::RawAllocatorDumpEntryValue::Tag:: - VALUE_UINT64; + return memory_instrumentation::mojom:: + RawAllocatorDumpEntryValueDataView::Tag::VALUE_UINT64; case base::trace_event::MemoryAllocatorDump::Entry::EntryType::kString: - return memory_instrumentation::mojom::RawAllocatorDumpEntryValue::Tag:: - VALUE_STRING; + return memory_instrumentation::mojom:: + RawAllocatorDumpEntryValueDataView::Tag::VALUE_STRING; } NOTREACHED(); - return memory_instrumentation::mojom::RawAllocatorDumpEntryValue::Tag:: - VALUE_UINT64; + return memory_instrumentation::mojom::RawAllocatorDumpEntryValueDataView:: + Tag::VALUE_UINT64; } static uint64_t value_uint64( diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc index 30cee82b9de..f8c11ab4dd8 100644 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc +++ b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.cc @@ -16,7 +16,7 @@ bool OSMetrics::FillProcessMemoryMaps(base::ProcessId pid, std::vector<mojom::VmRegionPtr> results; -#if defined(OS_MACOSX) +#if defined(OS_MAC) // On macOS, fetching all memory maps is very slow. See // https://crbug.com/826913 and https://crbug.com/1035401. results = GetProcessModules(pid); diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h index 1e95e345556..1fdd28fbef1 100644 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h +++ b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h @@ -19,18 +19,35 @@ FORWARD_DECLARE_TEST(ProfilingJsonExporterTest, MemoryMaps); namespace memory_instrumentation { +// This class provides synchronous access to memory metrics for a process with a +// given |pid|. These interfaces have platform-specific restrictions: +// * On Android, due to sandboxing restrictions, processes can only access +// memory metrics for themselves. Thus |pid| must be equal to getpid(). +// * On Linux, due to sandboxing restrictions, only the privileged browser +// process has access to memory metrics for sandboxed child processes. +// * On Fuchsia, due to the API expecting a ProcessId rather than a +// ProcessHandle, processes can only access memory metrics for themselves or +// for children of base::GetDefaultJob(). +// +// These restrictions mean that any code that wishes to be cross-platform +// compatible cannot synchronously obtain memory metrics for a |pid|. Instead, +// it must use the async MemoryInstrumentation methods. class COMPONENT_EXPORT( RESOURCE_COORDINATOR_PUBLIC_MEMORY_INSTRUMENTATION) OSMetrics { public: + // Fills |dump| with memory information about |pid|. See class comments for + // restrictions on |pid|. |dump.platform_private_footprint| must be allocated + // before calling this function. If |pid| is null, the pid of the current + // process is used static bool FillOSMemoryDump(base::ProcessId pid, mojom::RawOSMemDump* dump); static bool FillProcessMemoryMaps(base::ProcessId, mojom::MemoryMapOption, mojom::RawOSMemDump*); static std::vector<mojom::VmRegionPtr> GetProcessMemoryMaps(base::ProcessId); -#if defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) static void SetProcSmapsForTesting(FILE*); -#endif // defined(OS_LINUX) || defined(OS_ANDROID) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) private: FRIEND_TEST_ALL_PREFIXES(OSMetricsTest, ParseProcSmaps); @@ -40,11 +57,11 @@ class COMPONENT_EXPORT( FRIEND_TEST_ALL_PREFIXES(heap_profiling::ProfilingJsonExporterTest, MemoryMaps); -#if defined(OS_MACOSX) +#if defined(OS_MAC) static std::vector<mojom::VmRegionPtr> GetProcessModules(base::ProcessId); #endif -#if defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) // Provides information on the dump state of resident pages. These values are // written to logs. New enum values can be added, but existing enums must // never be renumbered or deleted and reused. @@ -79,7 +96,7 @@ class COMPONENT_EXPORT( // TODO(chiniforooshan): move to /base/process/process_metrics_linux.cc after // making sure that peak RSS is useful. static size_t GetPeakResidentSetSize(base::ProcessId pid); -#endif // defined(OS_LINUX) || defined(OS_ANDROID) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) }; } // namespace memory_instrumentation diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_fuchsia.cc b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_fuchsia.cc index 4a946b9427f..42d7d8622a5 100644 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_fuchsia.cc +++ b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_fuchsia.cc @@ -4,6 +4,15 @@ #include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h" +#include "base/process/process.h" +#include "base/process/process_handle.h" + +#include <lib/zx/job.h> +#include <lib/zx/object.h> +#include <lib/zx/process.h> +#include <zircon/limits.h> +#include <zircon/status.h> +#include <zircon/syscalls.h> #include <vector> namespace memory_instrumentation { @@ -11,9 +20,25 @@ namespace memory_instrumentation { // static bool OSMetrics::FillOSMemoryDump(base::ProcessId pid, mojom::RawOSMemDump* dump) { - // TODO(fuchsia): Implement this. See crbug.com/750948 - NOTIMPLEMENTED(); - return false; + base::Process process = pid == base::kNullProcessId + ? base::Process::Current() + : base::Process::Open(pid); + zx::unowned<zx::process> zx_process(process.Handle()); + zx_info_task_stats_t info; + zx_status_t status = zx_process->get_info(ZX_INFO_TASK_STATS, &info, + sizeof(info), nullptr, nullptr); + if (status != ZX_OK) { + return false; + } + + size_t rss_bytes = info.mem_private_bytes + info.mem_shared_bytes; + size_t rss_anon_bytes = info.mem_private_bytes; + + dump->resident_set_kb = rss_bytes / 1024; + dump->platform_private_footprint->rss_anon_bytes = rss_anon_bytes; + // Fuchsia has no swap. + dump->platform_private_footprint->vm_swap_bytes = 0; + return true; } // static diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc index 589a3a3fd06..f08b8507d0a 100644 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc +++ b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics_unittest.cc @@ -12,7 +12,7 @@ #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_MACOSX) +#if defined(OS_MAC) #include <libgen.h> #include <mach-o/dyld.h> #endif @@ -22,13 +22,13 @@ #include <windows.h> #endif -#if defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) #include <sys/mman.h> #endif namespace memory_instrumentation { -#if defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) namespace { const char kTestSmaps1[] = "00400000-004be000 r-xp 00000000 fc:01 1234 /file/1\n" @@ -128,7 +128,7 @@ void CreateTempFileWithContents(const char* contents, base::ScopedFILE* file) { } } // namespace -#endif // defined(OS_LINUX) || defined(OS_ANDROID) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) TEST(OSMetricsTest, GivesNonZeroResults) { base::ProcessId pid = base::kNullProcessId; @@ -136,16 +136,17 @@ TEST(OSMetricsTest, GivesNonZeroResults) { dump.platform_private_footprint = mojom::PlatformPrivateFootprint::New(); EXPECT_TRUE(OSMetrics::FillOSMemoryDump(pid, &dump)); EXPECT_TRUE(dump.platform_private_footprint); -#if defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) || \ + defined(OS_FUCHSIA) EXPECT_GT(dump.platform_private_footprint->rss_anon_bytes, 0u); #elif defined(OS_WIN) EXPECT_GT(dump.platform_private_footprint->private_bytes, 0u); -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) EXPECT_GT(dump.platform_private_footprint->internal_bytes, 0u); #endif } -#if defined(OS_LINUX) || defined(OS_ANDROID) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) TEST(OSMetricsTest, ParseProcSmaps) { const uint32_t kProtR = mojom::VmRegion::kProtectionFlagsRead; const uint32_t kProtW = mojom::VmRegion::kProtectionFlagsWrite; @@ -252,7 +253,7 @@ TEST(OSMetricsTest, GetMappedAndResidentPages) { EXPECT_EQ(pages == accessed_pages_set, true); } -#endif // defined(OS_LINUX) || defined(OS_ANDROID) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) #if defined(OS_WIN) void DummyFunction() {} @@ -305,7 +306,7 @@ TEST(OSMetricsTest, TestWinModuleReading) { } #endif // defined(OS_WIN) -#if defined(OS_MACOSX) +#if defined(OS_MAC) namespace { void CheckMachORegions(const std::vector<mojom::VmRegionPtr>& maps) { @@ -346,6 +347,6 @@ TEST(OSMetricsTest, DISABLED_TestMachOReading) { maps = OSMetrics::GetProcessModules(base::kNullProcessId); CheckMachORegions(maps); } -#endif // defined(OS_MACOSX) +#endif // defined(OS_MAC) } // namespace memory_instrumentation diff --git a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc index 03b977fdd87..221956c6306 100644 --- a/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc +++ b/chromium/services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.cc @@ -204,7 +204,7 @@ void TracingObserver::MemoryMapsAsValueInto( is_argument_filtering_enabled)); // The following stats are only well defined on Linux-derived OSes. -#if !defined(OS_MACOSX) && !defined(OS_WIN) +#if !defined(OS_MAC) && !defined(OS_WIN) value->BeginDictionary("bs"); // byte stats value->SetString( "pss", diff --git a/chromium/services/resource_coordinator/public/cpp/typemaps.gni b/chromium/services/resource_coordinator/public/cpp/typemaps.gni deleted file mode 100644 index e6ac78fe325..00000000000 --- a/chromium/services/resource_coordinator/public/cpp/typemaps.gni +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -typemaps = [ "//services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.typemap" ] diff --git a/chromium/services/resource_coordinator/public/mojom/BUILD.gn b/chromium/services/resource_coordinator/public/mojom/BUILD.gn index 91170ef50f3..ec433ce1e59 100644 --- a/chromium/services/resource_coordinator/public/mojom/BUILD.gn +++ b/chromium/services/resource_coordinator/public/mojom/BUILD.gn @@ -20,7 +20,54 @@ mojom_component("mojom") { ] enabled_features = [] - if (is_linux || is_android) { + if (is_linux || is_chromeos || is_android) { enabled_features += [ "private_swap_info" ] } + + cpp_typemaps = [ + { + types = [ + { + mojom = "memory_instrumentation.mojom.DumpType" + cpp = "::base::trace_event::MemoryDumpType" + }, + { + mojom = "memory_instrumentation.mojom.LevelOfDetail" + cpp = "::base::trace_event::MemoryDumpLevelOfDetail" + }, + { + mojom = "memory_instrumentation.mojom.Determinism" + cpp = "::base::trace_event::MemoryDumpDeterminism" + }, + { + mojom = "memory_instrumentation.mojom.RequestArgs" + cpp = "::base::trace_event::MemoryDumpRequestArgs" + }, + { + mojom = "memory_instrumentation.mojom.RawAllocatorDumpEdge" + cpp = + "::base::trace_event::ProcessMemoryDump::MemoryAllocatorDumpEdge" + }, + { + mojom = "memory_instrumentation.mojom.RawAllocatorDumpEntry" + cpp = "::base::trace_event::MemoryAllocatorDump::Entry" + move_only = true + }, + { + mojom = "memory_instrumentation.mojom.RawAllocatorDump" + cpp = "::std::unique_ptr<::base::trace_event::MemoryAllocatorDump>" + move_only = true + }, + { + mojom = "memory_instrumentation.mojom.RawProcessMemoryDump" + cpp = "::std::unique_ptr<::base::trace_event::ProcessMemoryDump>" + move_only = true + nullable_is_same_type = true + }, + ] + traits_headers = [ "//services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.h" ] + traits_sources = [ "//services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation_mojom_traits.cc" ] + traits_public_deps = [ "//base" ] + }, + ] } diff --git a/chromium/services/service_manager/BUILD.gn b/chromium/services/service_manager/BUILD.gn index 25d7a7c9e7c..7de1fb1d77f 100644 --- a/chromium/services/service_manager/BUILD.gn +++ b/chromium/services/service_manager/BUILD.gn @@ -48,9 +48,9 @@ source_set("service_manager") { public_deps = [ "//base", "//mojo/public/cpp/bindings", + "//sandbox/policy", "//services/service_manager/public/cpp", "//services/service_manager/public/mojom", - "//services/service_manager/sandbox", ] if (!is_ios) { @@ -65,7 +65,7 @@ source_set("service_manager") { ] } - if (is_linux) { + if (is_linux || is_chromeos) { deps += [ "//sandbox/linux:sandbox_services" ] } } diff --git a/chromium/services/service_manager/embedder/BUILD.gn b/chromium/services/service_manager/embedder/BUILD.gn index 6d28466ba7f..fbbf2166aae 100644 --- a/chromium/services/service_manager/embedder/BUILD.gn +++ b/chromium/services/service_manager/embedder/BUILD.gn @@ -30,7 +30,7 @@ if (!is_ios) { "mac_init.mm", ] - libs = [ "Foundation.framework" ] + frameworks = [ "Foundation.framework" ] } deps = [ @@ -58,7 +58,7 @@ if (!is_ios) { deps += [ "//ui/base" ] } - if (is_linux) { + if (is_linux || is_chromeos) { deps += [ ":set_process_title_linux" ] } @@ -79,7 +79,7 @@ source_set("embedder_result_codes") { sources = [ "result_codes.h" ] } -if (is_linux) { +if (is_linux || is_chromeos) { source_set("set_process_title_linux") { public = [ "set_process_title_linux.h" ] sources = [ "set_process_title_linux.cc" ] diff --git a/chromium/services/service_manager/embedder/main.cc b/chromium/services/service_manager/embedder/main.cc index d277d1eea17..607618c0fad 100644 --- a/chromium/services/service_manager/embedder/main.cc +++ b/chromium/services/service_manager/embedder/main.cc @@ -33,6 +33,7 @@ #include "mojo/core/embedder/embedder.h" #include "mojo/core/embedder/scoped_ipc_support.h" #include "mojo/public/cpp/base/shared_memory_utils.h" +#include "sandbox/policy/sandbox_type.h" #include "services/service_manager/embedder/main_delegate.h" #include "services/service_manager/embedder/process_type.h" #include "services/service_manager/embedder/set_process_title.h" @@ -41,7 +42,6 @@ #include "services/service_manager/public/cpp/service.h" #include "services/service_manager/public/cpp/service_executable/service_executable_environment.h" #include "services/service_manager/public/cpp/service_executable/switches.h" -#include "services/service_manager/sandbox/sandbox_type.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_switches.h" @@ -62,14 +62,14 @@ #include "base/posix/global_descriptors.h" #endif -#if defined(OS_MACOSX) +#if defined(OS_MAC) #include "base/mac/scoped_nsautorelease_pool.h" #include "services/service_manager/embedder/mac_init.h" #if BUILDFLAG(USE_ALLOCATOR_SHIM) #include "base/allocator/allocator_shim.h" #endif -#endif // defined(OS_MACOSX) +#endif // defined(OS_MAC) namespace service_manager { @@ -257,7 +257,7 @@ int Main(const MainParams& params) { int exit_code = -1; base::debug::GlobalActivityTracker* tracker = nullptr; ProcessType process_type = delegate->OverrideProcessType(); -#if defined(OS_MACOSX) +#if defined(OS_MAC) std::unique_ptr<base::mac::ScopedNSAutoreleasePool> autorelease_pool; #endif @@ -270,12 +270,12 @@ int Main(const MainParams& params) { #endif if (!is_initialized) { is_initialized = true; -#if defined(OS_MACOSX) && BUILDFLAG(USE_ALLOCATOR_SHIM) +#if defined(OS_MAC) && BUILDFLAG(USE_ALLOCATOR_SHIM) base::allocator::InitializeAllocatorShim(); #endif base::EnableTerminationOnOutOfMemory(); -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) // The various desktop environments set this environment variable that // allows the dbus client library to connect directly to the bus. When this // variable is not set (test environments like xvfb-run), the dbus client @@ -344,7 +344,7 @@ int Main(const MainParams& params) { MainDelegate::InitializeParams init_params; -#if defined(OS_MACOSX) +#if defined(OS_MAC) // We need this pool for all the objects created before we get to the event // loop, but we don't want to leave them hanging around until the app quits. // Each "main" needs to flush this pool right before it goes into its main @@ -386,9 +386,9 @@ int Main(const MainParams& params) { // Note #2: some platforms can directly allocated shared memory in a // sandboxed process. The defines below must be in sync with the // implementation of mojo::NodeController::CreateSharedBuffer(). -#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI) && !defined(OS_FUCHSIA) - if (service_manager::IsUnsandboxedSandboxType( - service_manager::SandboxTypeFromCommandLine(command_line))) { +#if !defined(OS_MAC) && !defined(OS_NACL_SFI) && !defined(OS_FUCHSIA) + if (sandbox::policy::IsUnsandboxedSandboxType( + sandbox::policy::SandboxTypeFromCommandLine(command_line))) { // Unsandboxed processes don't need shared memory brokering... because // they're not sandboxed. } else if (mojo_config.force_direct_shared_memory_allocation) { @@ -402,7 +402,7 @@ int Main(const MainParams& params) { // allocate shared memory. mojo::SharedMemoryUtils::InstallBaseHooks(); } -#endif // !defined(OS_MACOSX) && !defined(OS_NACL_SFI) && !defined(OS_FUCHSIA) +#endif // !defined(OS_MAC) && !defined(OS_NACL_SFI) && !defined(OS_FUCHSIA) #if defined(OS_WIN) // Route stdio to parent console (if any) or create one. @@ -465,7 +465,7 @@ int Main(const MainParams& params) { } } -#if defined(OS_MACOSX) +#if defined(OS_MAC) autorelease_pool.reset(); #endif diff --git a/chromium/services/service_manager/embedder/main_delegate.h b/chromium/services/service_manager/embedder/main_delegate.h index abced50cb03..fc3d4538059 100644 --- a/chromium/services/service_manager/embedder/main_delegate.h +++ b/chromium/services/service_manager/embedder/main_delegate.h @@ -36,7 +36,7 @@ class COMPONENT_EXPORT(SERVICE_MANAGER_EMBEDDER) MainDelegate { public: // Extra parameters passed to MainDelegate::Initialize. struct InitializeParams { -#if defined(OS_MACOSX) +#if defined(OS_MAC) // The outermost autorelease pool, allocated by internal service manager // logic. This is guaranteed to live throughout the extent of Run(). base::mac::ScopedNSAutoreleasePool* autorelease_pool = nullptr; diff --git a/chromium/services/service_manager/embedder/set_process_title.cc b/chromium/services/service_manager/embedder/set_process_title.cc index 1dc53b847ef..a2ced5316c7 100644 --- a/chromium/services/service_manager/embedder/set_process_title.cc +++ b/chromium/services/service_manager/embedder/set_process_title.cc @@ -2,13 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// Define _GNU_SOURCE to ensure that <errno.h> defines +// program_invocation_short_name. Keep this at the top of the file since some +// system headers might include <errno.h> and the header could be skipped on +// subsequent includes. +#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + #include "services/service_manager/embedder/set_process_title.h" #include <stddef.h> #include "build/build_config.h" -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_SOLARIS) +#if defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_SOLARIS) #include <limits.h> #include <stdlib.h> #include <unistd.h> @@ -16,9 +24,10 @@ #include <string> #include "base/command_line.h" -#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_SOLARIS) +#endif // defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_SOLARIS) -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#include <errno.h> // Get program_invocation_short_name declaration. #include <sys/prctl.h> #include "base/files/file_path.h" @@ -28,12 +37,12 @@ #include "base/threading/platform_thread.h" // Linux/glibc doesn't natively have setproctitle(). #include "services/service_manager/embedder/set_process_title_linux.h" -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) namespace service_manager { // TODO(jrg): Find out if setproctitle or equivalent is available on Android. -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_SOLARIS) && \ +#if defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_SOLARIS) && \ !defined(OS_ANDROID) && !defined(OS_FUCHSIA) void SetProcessTitleFromCommandLine(const char** main_argv) { @@ -43,7 +52,7 @@ void SetProcessTitleFromCommandLine(const char** main_argv) { std::string title; bool have_argv0 = false; -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) DCHECK_EQ(base::PlatformThread::CurrentId(), getpid()); if (main_argv) @@ -64,13 +73,19 @@ void SetProcessTitleFromCommandLine(const char** main_argv) { if (base::EndsWith(title, kDeletedSuffix, base::CompareCase::SENSITIVE)) title.resize(title.size() - kDeletedSuffix.size()); + base::FilePath::StringType base_name = + base::FilePath(title).BaseName().value(); // PR_SET_NAME is available in Linux 2.6.9 and newer. // When available at run time, this sets the short process name that shows // when the full command line is not being displayed in most process // listings. - prctl(PR_SET_NAME, base::FilePath(title).BaseName().value().c_str()); + prctl(PR_SET_NAME, base_name.c_str()); + + // This prevents program_invocation_short_name from being broken by + // setproctitle(). + program_invocation_short_name = strdup(base_name.c_str()); } -#endif // defined(OS_LINUX) +#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); diff --git a/chromium/services/service_manager/public/cpp/BUILD.gn b/chromium/services/service_manager/public/cpp/BUILD.gn index e7d0f383a25..5220a379e5f 100644 --- a/chromium/services/service_manager/public/cpp/BUILD.gn +++ b/chromium/services/service_manager/public/cpp/BUILD.gn @@ -25,11 +25,11 @@ component("cpp") { "manifest_builder.h", "service.cc", "service.h", - "service_binding.cc", - "service_binding.h", "service_context_ref.h", "service_keepalive.cc", "service_keepalive.h", + "service_receiver.cc", + "service_receiver.h", "standalone_connector_impl.cc", "standalone_connector_impl.h", ] diff --git a/chromium/services/service_manager/public/cpp/OWNERS b/chromium/services/service_manager/public/cpp/OWNERS index 7aebc8abbf8..d5fefd82012 100644 --- a/chromium/services/service_manager/public/cpp/OWNERS +++ b/chromium/services/service_manager/public/cpp/OWNERS @@ -1,4 +1,2 @@ per-file *_mojom_traits*.*=set noparent per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS diff --git a/chromium/services/service_manager/public/cpp/bind_source_info.typemap b/chromium/services/service_manager/public/cpp/bind_source_info.typemap deleted file mode 100644 index 120f6d9dc07..00000000000 --- a/chromium/services/service_manager/public/cpp/bind_source_info.typemap +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//services/service_manager/public/mojom/service.mojom" -public_headers = [ "//services/service_manager/public/cpp/bind_source_info.h" ] -traits_headers = - [ "//services/service_manager/public/cpp/bind_source_info_mojom_traits.h" ] -public_deps = [ - "//services/service_manager/public/cpp:cpp_types", -] - -type_mappings = - [ "service_manager.mojom.BindSourceInfo=::service_manager::BindSourceInfo" ] diff --git a/chromium/services/service_manager/public/cpp/bind_source_info_mojom_traits.h b/chromium/services/service_manager/public/cpp/bind_source_info_mojom_traits.h index 9f57eec34b6..744fb6895b5 100644 --- a/chromium/services/service_manager/public/cpp/bind_source_info_mojom_traits.h +++ b/chromium/services/service_manager/public/cpp/bind_source_info_mojom_traits.h @@ -6,13 +6,15 @@ #define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_BIND_SOURCE_INFO_MOJOM_TRAITS_H_ #include "services/service_manager/public/cpp/bind_source_info.h" -#include "services/service_manager/public/mojom/service.mojom.h" +#include "services/service_manager/public/cpp/identity_mojom_traits.h" +#include "services/service_manager/public/cpp/interface_provider_spec_mojom_traits.h" +#include "services/service_manager/public/mojom/service.mojom-shared.h" namespace mojo { template <> struct COMPONENT_EXPORT(SERVICE_MANAGER_MOJOM) - StructTraits<service_manager::mojom::BindSourceInfo::DataView, + StructTraits<service_manager::mojom::BindSourceInfoDataView, service_manager::BindSourceInfo> { static const service_manager::Identity& identity( const service_manager::BindSourceInfo& source) { diff --git a/chromium/services/service_manager/public/cpp/identity.typemap b/chromium/services/service_manager/public/cpp/identity.typemap deleted file mode 100644 index 6fb23dc509d..00000000000 --- a/chromium/services/service_manager/public/cpp/identity.typemap +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//services/service_manager/public/mojom/connector.mojom" -public_headers = [ "//services/service_manager/public/cpp/identity.h" ] -traits_headers = - [ "//services/service_manager/public/cpp/identity_mojom_traits.h" ] -public_deps = [ - "//services/service_manager/public/cpp:cpp_types", - "//services/service_manager/public/cpp:mojom_traits", -] - -type_mappings = [ "service_manager.mojom.Identity=::service_manager::Identity" ] diff --git a/chromium/services/service_manager/public/cpp/interface_provider_spec.typemap b/chromium/services/service_manager/public/cpp/interface_provider_spec.typemap deleted file mode 100644 index 72f0278bb34..00000000000 --- a/chromium/services/service_manager/public/cpp/interface_provider_spec.typemap +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//services/service_manager/public/mojom/interface_provider_spec.mojom" -public_headers = - [ "//services/service_manager/public/cpp/interface_provider_spec.h" ] -traits_headers = [ "//services/service_manager/public/cpp/interface_provider_spec_mojom_traits.h" ] -public_deps = [ - "//services/service_manager/public/cpp:cpp_types", -] - -type_mappings = [ - "service_manager.mojom.InterfaceProviderSpec=::service_manager::InterfaceProviderSpec", - "service_manager.mojom.InterfaceSet=::service_manager::InterfaceSet", - "service_manager.mojom.CapabilitySet=::service_manager::CapabilitySet", -] diff --git a/chromium/services/service_manager/public/cpp/interface_provider_spec_mojom_traits.h b/chromium/services/service_manager/public/cpp/interface_provider_spec_mojom_traits.h index e7c8a8feaed..b996bc7b001 100644 --- a/chromium/services/service_manager/public/cpp/interface_provider_spec_mojom_traits.h +++ b/chromium/services/service_manager/public/cpp/interface_provider_spec_mojom_traits.h @@ -6,13 +6,13 @@ #define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_INTERFACE_PROVIDER_SPEC_MOJOM_TRAITS_H_ #include "services/service_manager/public/cpp/interface_provider_spec.h" -#include "services/service_manager/public/mojom/interface_provider_spec.mojom.h" +#include "services/service_manager/public/mojom/interface_provider_spec.mojom-shared.h" namespace mojo { template <> struct COMPONENT_EXPORT(SERVICE_MANAGER_MOJOM) - StructTraits<service_manager::mojom::InterfaceProviderSpec::DataView, + StructTraits<service_manager::mojom::InterfaceProviderSpecDataView, service_manager::InterfaceProviderSpec> { static const std::map<service_manager::Capability, service_manager::InterfaceSet>& @@ -32,7 +32,7 @@ struct COMPONENT_EXPORT(SERVICE_MANAGER_MOJOM) template <> struct COMPONENT_EXPORT(SERVICE_MANAGER_MOJOM) - StructTraits<service_manager::mojom::InterfaceSet::DataView, + StructTraits<service_manager::mojom::InterfaceSetDataView, service_manager::InterfaceSet> { static std::vector<std::string> interfaces( const service_manager::InterfaceSet& spec) { @@ -57,7 +57,7 @@ struct COMPONENT_EXPORT(SERVICE_MANAGER_MOJOM) template <> struct COMPONENT_EXPORT(SERVICE_MANAGER_MOJOM) - StructTraits<service_manager::mojom::CapabilitySet::DataView, + StructTraits<service_manager::mojom::CapabilitySetDataView, service_manager::CapabilitySet> { static std::vector<std::string> capabilities( const service_manager::CapabilitySet& spec) { diff --git a/chromium/services/service_manager/public/cpp/manifest.h b/chromium/services/service_manager/public/cpp/manifest.h index 586441b2826..b731d58022e 100644 --- a/chromium/services/service_manager/public/cpp/manifest.h +++ b/chromium/services/service_manager/public/cpp/manifest.h @@ -93,7 +93,8 @@ struct COMPONENT_EXPORT(SERVICE_MANAGER_CPP) Manifest { // binary (for example Chromium, or any Content embedder), and the embedder // handles requests for new instances of the service via // ServiceProcess::Delegate::RunService(). The service will always run in - // a child process sandboxed according to SandboxType (see Options below). + // a child process sandboxed according to sandbox::policy::SandboxType (see + // Options below). kOutOfProcessBuiltin, // The service is launched out-of-process from a standalone service @@ -102,7 +103,8 @@ struct COMPONENT_EXPORT(SERVICE_MANAGER_CPP) Manifest { // "${service_name}.service.exe" on Windows). // // Proper sandboxing is currently not supported for standalone service - // executables, so SandboxType (see Options below) is ignored. This renders + // executables, so sandbox::policy::SandboxType (see Options below) is + // ignored. This renders // standalone service executables generally unsuitable for production // environments. kStandaloneExecutable, @@ -151,7 +153,8 @@ struct COMPONENT_EXPORT(SERVICE_MANAGER_CPP) Manifest { // if |execution_mode| is |kOutOfProcessBuiltin| or // |kStandaloneExecutable|. // - // TODO(https://crbug.com/915806): Make this field a SandboxType enum. + // TODO(https://crbug.com/915806): Make this field a + // sandbox::policy::SandboxType enum. std::string sandbox_type{"utility"}; }; diff --git a/chromium/services/service_manager/public/cpp/service.h b/chromium/services/service_manager/public/cpp/service.h index 9316c3aa4b1..38fb7950b51 100644 --- a/chromium/services/service_manager/public/cpp/service.h +++ b/chromium/services/service_manager/public/cpp/service.h @@ -32,7 +32,7 @@ class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) Service { // default implementation of |OnDisconnected()|. // // This should really only be called on a Service instance that has a bound - // connection to the Service Manager, e.g. a functioning ServiceBinding. If + // connection to the Service Manager, e.g. a functioning ServiceReceiver. If // the service never calls |Terminate()|, it will effectively leak. // // If |callback| is non-null, it will be invoked after |service| is destroyed. diff --git a/chromium/services/service_manager/public/cpp/service_executable/BUILD.gn b/chromium/services/service_manager/public/cpp/service_executable/BUILD.gn index d62e2b37543..4c1c4969ef6 100644 --- a/chromium/services/service_manager/public/cpp/service_executable/BUILD.gn +++ b/chromium/services/service_manager/public/cpp/service_executable/BUILD.gn @@ -13,8 +13,8 @@ source_set("support") { "//mojo/core/embedder", "//mojo/public/cpp/platform", "//mojo/public/cpp/system", + "//sandbox/policy", "//services/service_manager/public/cpp", - "//services/service_manager/sandbox", ] public_deps = [ @@ -22,7 +22,7 @@ source_set("support") { "//services/service_manager/public/mojom", ] - if (is_linux) { + if (is_linux || is_chromeos) { deps += [ "//sandbox/linux:sandbox", "//sandbox/linux:sandbox_services", diff --git a/chromium/services/service_manager/public/cpp/service_executable/main.cc b/chromium/services/service_manager/public/cpp/service_executable/main.cc index 5cec8822602..e760c26199a 100644 --- a/chromium/services/service_manager/public/cpp/service_executable/main.cc +++ b/chromium/services/service_manager/public/cpp/service_executable/main.cc @@ -23,7 +23,7 @@ #include "services/service_manager/public/cpp/service_executable/service_main.h" #include "services/service_manager/public/mojom/service.mojom.h" -#if defined(OS_MACOSX) +#if defined(OS_MAC) #include "base/mac/bundle_locations.h" #endif diff --git a/chromium/services/service_manager/public/cpp/service_executable/service_executable_environment.cc b/chromium/services/service_manager/public/cpp/service_executable/service_executable_environment.cc index 7d660a916fb..8d99a8e2773 100644 --- a/chromium/services/service_manager/public/cpp/service_executable/service_executable_environment.cc +++ b/chromium/services/service_manager/public/cpp/service_executable/service_executable_environment.cc @@ -6,48 +6,49 @@ #include "base/check.h" #include "base/command_line.h" -#include "base/message_loop/message_loop_current.h" #include "base/message_loop/message_pump_type.h" #include "base/synchronization/waitable_event.h" +#include "base/task/current_thread.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "build/build_config.h" #include "mojo/core/embedder/embedder.h" #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/invitation.h" #include "mojo/public/cpp/system/message_pipe.h" +#include "sandbox/policy/sandbox.h" +#include "sandbox/policy/switches.h" #include "services/service_manager/public/cpp/service_executable/switches.h" -#include "services/service_manager/sandbox/sandbox.h" -#include "services/service_manager/sandbox/switches.h" -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) #include "base/rand_util.h" #include "base/system/sys_info.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" +#include "sandbox/policy/linux/sandbox_linux.h" #endif namespace service_manager { ServiceExecutableEnvironment::ServiceExecutableEnvironment() : ipc_thread_("IPC Thread") { - DCHECK(!base::MessageLoopCurrent::Get()); + DCHECK(!base::CurrentThread::Get()); -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kServiceSandboxType)) { + if (command_line.HasSwitch(sandbox::policy::switches::kServiceSandboxType)) { // Warm parts of base in the copy of base in the mojo runner. base::RandUint64(); base::SysInfo::AmountOfPhysicalMemory(); base::SysInfo::NumberOfProcessors(); // Repeat steps normally performed by the zygote. - SandboxLinux::Options sandbox_options; + sandbox::policy::SandboxLinux::Options sandbox_options; sandbox_options.engage_namespace_sandbox = true; - Sandbox::Initialize( - UtilitySandboxTypeFromString( - command_line.GetSwitchValueASCII(switches::kServiceSandboxType)), - SandboxLinux::PreSandboxHook(), sandbox_options); + sandbox::policy::Sandbox::Initialize( + sandbox::policy::UtilitySandboxTypeFromString( + command_line.GetSwitchValueASCII( + sandbox::policy::switches::kServiceSandboxType)), + sandbox::policy::SandboxLinux::PreSandboxHook(), sandbox_options); } #endif @@ -64,12 +65,12 @@ ServiceExecutableEnvironment::ServiceExecutableEnvironment() ServiceExecutableEnvironment::~ServiceExecutableEnvironment() = default; -mojom::ServiceRequest +mojo::PendingReceiver<mojom::Service> ServiceExecutableEnvironment::TakeServiceRequestFromCommandLine() { auto invitation = mojo::IncomingInvitation::Accept( mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine( *base::CommandLine::ForCurrentProcess())); - return mojom::ServiceRequest(invitation.ExtractMessagePipe( + return mojo::PendingReceiver<mojom::Service>(invitation.ExtractMessagePipe( base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kServiceRequestAttachmentName))); } diff --git a/chromium/services/service_manager/public/cpp/service_executable/service_executable_environment.h b/chromium/services/service_manager/public/cpp/service_executable/service_executable_environment.h index f5265705ed0..0183aa0d2da 100644 --- a/chromium/services/service_manager/public/cpp/service_executable/service_executable_environment.h +++ b/chromium/services/service_manager/public/cpp/service_executable/service_executable_environment.h @@ -9,6 +9,7 @@ #include "base/optional.h" #include "base/threading/thread.h" #include "mojo/core/embedder/scoped_ipc_support.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" #include "services/service_manager/public/mojom/service.mojom.h" namespace service_manager { @@ -32,7 +33,7 @@ class ServiceExecutableEnvironment { // Returns a ServiceRequest which should be passed to the Service // implementation which will run within the extent of this environment. - mojom::ServiceRequest TakeServiceRequestFromCommandLine(); + mojo::PendingReceiver<mojom::Service> TakeServiceRequestFromCommandLine(); private: base::Thread ipc_thread_; diff --git a/chromium/services/service_manager/public/cpp/service_executable/service_main.h b/chromium/services/service_manager/public/cpp/service_executable/service_main.h index cdf2dc0e46b..534a1274cd5 100644 --- a/chromium/services/service_manager/public/cpp/service_executable/service_main.h +++ b/chromium/services/service_manager/public/cpp/service_executable/service_main.h @@ -10,6 +10,7 @@ // Service executables linking against the // "//services/service_manager/public/cpp/service_executable:main" target must // implement this function as their entry point. -void ServiceMain(service_manager::mojom::ServiceRequest request); +void ServiceMain( + mojo::PendingReceiver<service_manager::mojom::Service> receiver); #endif // SERVICES_SERVICE_MANAGER_PUBLIC_CPP_STANDALONE_SERVICE_SERVICE_MAIN_H_ diff --git a/chromium/services/service_manager/public/cpp/service_executable/switches.cc b/chromium/services/service_manager/public/cpp/service_executable/switches.cc index 9a84d6c9faa..88be354d542 100644 --- a/chromium/services/service_manager/public/cpp/service_executable/switches.cc +++ b/chromium/services/service_manager/public/cpp/service_executable/switches.cc @@ -12,8 +12,9 @@ namespace switches { // different services. const char kServiceName[] = "service-name"; -// The name of the |service_manager::mojom::ServiceRequest| message pipe handle -// that is attached to the incoming Mojo invitation received by the service. +// The name of the |mojo::PendingReceiver<service_manager::mojom::Service>| +// message pipe handle that is attached to the incoming Mojo invitation received +// by the service. const char kServiceRequestAttachmentName[] = "service-request-attachment-name"; } // namespace switches diff --git a/chromium/services/service_manager/public/cpp/service_filter.typemap b/chromium/services/service_manager/public/cpp/service_filter.typemap deleted file mode 100644 index 1b30da43555..00000000000 --- a/chromium/services/service_manager/public/cpp/service_filter.typemap +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2018 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//services/service_manager/public/mojom/service_filter.mojom" -public_headers = [ "//services/service_manager/public/cpp/service_filter.h" ] -traits_headers = - [ "//services/service_manager/public/cpp/service_filter_mojom_traits.h" ] -public_deps = [ - "//services/service_manager/public/cpp:cpp_types", - "//services/service_manager/public/cpp:mojom_traits", -] -type_mappings = - [ "service_manager.mojom.ServiceFilter=::service_manager::ServiceFilter" ] diff --git a/chromium/services/service_manager/public/cpp/service_keepalive.cc b/chromium/services/service_manager/public/cpp/service_keepalive.cc index a4e53956313..0e02994a207 100644 --- a/chromium/services/service_manager/public/cpp/service_keepalive.cc +++ b/chromium/services/service_manager/public/cpp/service_keepalive.cc @@ -6,7 +6,7 @@ #include "base/bind.h" #include "base/task/post_task.h" -#include "services/service_manager/public/cpp/service_binding.h" +#include "services/service_manager/public/cpp/service_receiver.h" namespace service_manager { @@ -56,9 +56,9 @@ class ServiceKeepaliveRefImpl : public ServiceKeepaliveRef { DISALLOW_COPY_AND_ASSIGN(ServiceKeepaliveRefImpl); }; -ServiceKeepalive::ServiceKeepalive(ServiceBinding* binding, +ServiceKeepalive::ServiceKeepalive(ServiceReceiver* receiver, base::Optional<base::TimeDelta> idle_timeout) - : binding_(binding), idle_timeout_(idle_timeout) {} + : receiver_(receiver), idle_timeout_(idle_timeout) {} ServiceKeepalive::~ServiceKeepalive() = default; @@ -111,10 +111,10 @@ void ServiceKeepalive::OnTimerExpired() { for (auto& observer : observers_) observer.OnIdleTimeout(); - // NOTE: We allow for a null |binding_| because it's convenient in some + // NOTE: We allow for a null |receiver_| because it's convenient in some // testing scenarios and adds no real complexity to this implementation. - if (binding_) - binding_->RequestClose(); + if (receiver_) + receiver_->RequestClose(); } } // namespace service_manager diff --git a/chromium/services/service_manager/public/cpp/service_keepalive.h b/chromium/services/service_manager/public/cpp/service_keepalive.h index 6a1488993d7..70ab6ff9964 100644 --- a/chromium/services/service_manager/public/cpp/service_keepalive.h +++ b/chromium/services/service_manager/public/cpp/service_keepalive.h @@ -15,12 +15,12 @@ namespace service_manager { -class ServiceBinding; +class ServiceReceiver; class ServiceKeepaliveRef; // Service implementations are responsible for managing their own lifetime and -// as such are expected to call |ServiceBinding::RequestClose()| on their own -// ServiceBinding when they are no longer in use by any clients and otherwise +// as such are expected to call |ServiceReceiver::RequestClose()| on their own +// ServiceReceiver when they are no longer in use by any clients and otherwise // have no reason to keep running (e.g. no active UI visible). // // ServiceKeepalive helps Service implementations accomplish this by vending @@ -32,7 +32,7 @@ class ServiceKeepaliveRef; // If the ServiceKeepalive's number of living ServiceKeepaliveRef instances goes // to zero, the service is considered idle. If the ServiceKeepalive is // configured with an idle timeout, it will automatically invoke -// |ServiceBinding::RequestClose()| on its associated ServiceBinding once the +// |ServiceReceiver::RequestClose()| on its associated ServiceReceiver once the // service has remained idle for that continuous duration. // // Services can use this mechanism to vend ServiceKeepaliveRefs to various parts @@ -55,10 +55,10 @@ class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceKeepalive { }; // Constructs a ServiceKeepalive to control the lifetime behavior of - // |*binding|. Note that if either |binding| or |idle_timeout| is null, this + // |*receiver|. Note that if either |receiver| or |idle_timeout| is null, this // object will not do any automatic lifetime management and will instead only // maintain an internal ref-count which the consumer can query. - ServiceKeepalive(ServiceBinding* binding, + ServiceKeepalive(ServiceReceiver* receiver, base::Optional<base::TimeDelta> idle_timeout); ~ServiceKeepalive(); @@ -82,7 +82,7 @@ class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceKeepalive { void OnTimerExpired(); - ServiceBinding* const binding_; + ServiceReceiver* const receiver_; const base::Optional<base::TimeDelta> idle_timeout_; base::Optional<base::OneShotTimer> idle_timer_; base::ObserverList<Observer> observers_; @@ -93,7 +93,7 @@ class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceKeepalive { }; // Objects which can be created by a |ServiceKeepalive| and cloned from each -// other. The ServiceBinding referenced by a ServiceKeepalive is considered +// other. The ServiceReceiver referenced by a ServiceKeepalive is considered // active as long as one of these objects exists and is associated with that // ServiceKeepalive. class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceKeepaliveRef { diff --git a/chromium/services/service_manager/public/cpp/service_binding.cc b/chromium/services/service_manager/public/cpp/service_receiver.cc index 83ba0279530..18ac3893fa5 100644 --- a/chromium/services/service_manager/public/cpp/service_binding.cc +++ b/chromium/services/service_manager/public/cpp/service_receiver.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "services/service_manager/public/cpp/service_binding.h" +#include "services/service_manager/public/cpp/service_receiver.h" #include <utility> @@ -14,35 +14,35 @@ namespace service_manager { -ServiceBinding::ServiceBinding(service_manager::Service* service) +ServiceReceiver::ServiceReceiver(service_manager::Service* service) : service_(service) { DCHECK(service_); } -ServiceBinding::ServiceBinding(service_manager::Service* service, - mojo::PendingReceiver<mojom::Service> receiver) - : ServiceBinding(service) { +ServiceReceiver::ServiceReceiver(service_manager::Service* service, + mojo::PendingReceiver<mojom::Service> receiver) + : ServiceReceiver(service) { if (receiver.is_valid()) Bind(std::move(receiver)); } -ServiceBinding::~ServiceBinding() = default; +ServiceReceiver::~ServiceReceiver() = default; -Connector* ServiceBinding::GetConnector() { +Connector* ServiceReceiver::GetConnector() { if (!connector_) connector_ = Connector::Create(&pending_connector_receiver_); return connector_.get(); } -void ServiceBinding::Bind(mojo::PendingReceiver<mojom::Service> receiver) { +void ServiceReceiver::Bind(mojo::PendingReceiver<mojom::Service> receiver) { DCHECK(!is_bound()); receiver_.Bind(std::move(receiver)); receiver_.set_disconnect_handler(base::BindOnce( - &ServiceBinding::OnConnectionError, base::Unretained(this))); + &ServiceReceiver::OnConnectionError, base::Unretained(this))); } -void ServiceBinding::RequestClose() { - // We allow for innoccuous RequestClose() calls on unbound ServiceBindings. +void ServiceReceiver::RequestClose() { + // We allow for innoccuous RequestClose() calls on unbound ServiceReceivers. // This may occur e.g. when running a service in-process. if (!is_bound()) return; @@ -58,19 +58,19 @@ void ServiceBinding::RequestClose() { } } -void ServiceBinding::Close() { +void ServiceReceiver::Close() { DCHECK(is_bound()); receiver_.reset(); service_control_.reset(); connector_.reset(); } -void ServiceBinding::OnConnectionError() { +void ServiceReceiver::OnConnectionError() { service_->OnDisconnected(); } -void ServiceBinding::OnStart(const Identity& identity, - OnStartCallback callback) { +void ServiceReceiver::OnStart(const Identity& identity, + OnStartCallback callback) { identity_ = identity; if (!pending_connector_receiver_.is_valid()) @@ -85,7 +85,7 @@ void ServiceBinding::OnStart(const Identity& identity, service_control_->RequestQuit(); } -void ServiceBinding::OnBindInterface( +void ServiceReceiver::OnBindInterface( const BindSourceInfo& source_info, const std::string& interface_name, mojo::ScopedMessagePipeHandle interface_pipe, @@ -96,7 +96,7 @@ void ServiceBinding::OnBindInterface( service_->OnConnect(source_info, interface_name, std::move(interface_pipe)); } -void ServiceBinding::CreatePackagedServiceInstance( +void ServiceReceiver::CreatePackagedServiceInstance( const Identity& identity, mojo::PendingReceiver<mojom::Service> receiver, mojo::PendingRemote<mojom::ProcessMetadata> metadata) { diff --git a/chromium/services/service_manager/public/cpp/service_binding.h b/chromium/services/service_manager/public/cpp/service_receiver.h index 6a2f6c6f14e..c80300876f1 100644 --- a/chromium/services/service_manager/public/cpp/service_binding.h +++ b/chromium/services/service_manager/public/cpp/service_receiver.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_BINDING_H_ -#define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_BINDING_H_ +#ifndef SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_RECEIVER_H_ +#define SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_RECEIVER_H_ #include <memory> @@ -30,13 +30,14 @@ class Service; // implementation, while also exposing a working Connector interface the service // can use to make outgoing interface requests. // -// A ServiceBinding is considered to be "bound" after |Bind()| is invoked with a -// valid Service receiver (or the equivalent constructor is used -- see below). -// Upon connection error or an explicit call to |Close()|, the ServiceBinding -// will be considered "unbound" until another call to |Bind()| is made. +// A ServiceReceiver is considered to be "bound" after |Bind()| is invoked with +// a valid Service receiver (or the equivalent constructor is used -- see +// below). Upon connection error or an explicit call to |Close()|, the +// ServiceReceiver will be considered "unbound" until another call to |Bind()| +// is made. // -// NOTE: A well-behaved service should aim to always close its ServiceBinding -// gracefully by calling |RequestClose()|. Closing a ServiceBinding abruptly +// NOTE: A well-behaved service should aim to always close its ServiceReceiver +// gracefully by calling |RequestClose()|. Closing a ServiceReceiver abruptly // (by either destroying it or explicitly calling |Close()|) introduces inherent // flakiness into the system unless the Service's |OnDisconnected()| has already // been invoked, because otherwise the Service Manager may have in-flight @@ -44,45 +45,45 @@ class Service; // dropped to the dismay of the service instance which issued them. Exceptions // can reasonably be made for system-wide shutdown situations where even the // Service Manager itself will be imminently torn down. -class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceBinding +class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceReceiver : public mojom::Service { public: - // Creates a new ServiceBinding bound to |service|. The service will not + // Creates a new ServiceReceiver bound to |service|. The service will not // receive any Service interface calls until |Bind()| is called, but its // |connector()| is usable immediately upon construction. // - // |service| is not owned and must outlive this ServiceBinding. - explicit ServiceBinding(service_manager::Service* service); + // |service| is not owned and must outlive this ServiceReceiver. + explicit ServiceReceiver(service_manager::Service* service); // Same as above, but behaves as if |Bind(receiver)| is also called // immediately after construction. See below. - ServiceBinding(service_manager::Service* service, - mojo::PendingReceiver<mojom::Service> receiver); + ServiceReceiver(service_manager::Service* service, + mojo::PendingReceiver<mojom::Service> receiver); - ~ServiceBinding() override; + ~ServiceReceiver() override; bool is_bound() const { return receiver_.is_bound(); } const Identity& identity() const { return identity_; } // Returns a usable Connector which can make outgoing interface requests - // identifying as the service to which this ServiceBinding is bound. + // identifying as the service to which this ServiceReceiver is bound. Connector* GetConnector(); - // Binds this ServiceBinding to a new Service receiver. Once a ServiceBinding - // is bound, its target Service will begin receiving Service events. The - // order of events received is: + // Binds this ServiceReceiver to a new Service receiver. Once a + // ServiceReceiver is bound, its target Service will begin receiving Service + // events. The order of events received is: // // - OnStart() exactly once // - OnIdentityKnown() exactly once // - OnBindInterface() zero or more times // // The target Service will be able to receive these events until this - // ServiceBinding is either unbound or destroyed. + // ServiceReceiver is either unbound or destroyed. // // If |receiver| is invalid, this call does nothing. // - // Must only be called on an unbound ServiceBinding. + // Must only be called on an unbound ServiceReceiver. void Bind(mojo::PendingReceiver<mojom::Service> receiver); // Asks the Service Manager nicely if it's OK for this service instance to @@ -90,16 +91,16 @@ class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceBinding // binding's connection, ultimately triggering an |OnDisconnected()| call on // the bound Service object. // - // Must only be called on a bound ServiceBinding. + // Must only be called on a bound ServiceReceiver. void RequestClose(); // Immediately severs the connection to the Service Manager. No further - // incoming interface requests will be received until this ServiceBinding is + // incoming interface requests will be received until this ServiceReceiver is // bound again. Always prefer |RequestClose()| under normal circumstances, // unless |OnDisconnected()| has already been invoked on the Service. See the // note in the class documentation above regarding graceful binding closure. // - // Must only be called on a bound ServiceBinding. + // Must only be called on a bound ServiceReceiver. void Close(); private: @@ -118,13 +119,13 @@ class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceBinding // The Service instance to which all incoming events from the Service Manager // should be directed. Typically this is the object which owns this - // ServiceBinding. + // ServiceReceiver. service_manager::Service* const service_; // A pending Connector request which will eventually be passed to the Service - // Manager. Created preemptively by every unbound ServiceBinding so that + // Manager. Created preemptively by every unbound ServiceReceiver so that // |connector()| may begin pipelining outgoing requests even before the - // ServiceBinding is bound to a Service receiver. + // ServiceReceiver is bound to a Service receiver. mojo::PendingReceiver<mojom::Connector> pending_connector_receiver_; mojo::Receiver<mojom::Service> receiver_{this}; @@ -136,13 +137,13 @@ class COMPONENT_EXPORT(SERVICE_MANAGER_CPP) ServiceBinding mojo::AssociatedRemote<mojom::ServiceControl> service_control_; // Tracks whether |RequestClose()| has been called at least once prior to - // receiving |OnStart()| on a bound ServiceBinding. This ensures that the + // receiving |OnStart()| on a bound ServiceReceiver. This ensures that the // closure request is actually issued once |OnStart()| is invoked. bool request_closure_on_start_ = false; - DISALLOW_COPY_AND_ASSIGN(ServiceBinding); + DISALLOW_COPY_AND_ASSIGN(ServiceReceiver); }; } // namespace service_manager -#endif // SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_CONTEXT_H_ +#endif // SERVICES_SERVICE_MANAGER_PUBLIC_CPP_SERVICE_RECEIVER_H_ diff --git a/chromium/services/service_manager/public/cpp/typemaps.gni b/chromium/services/service_manager/public/cpp/typemaps.gni deleted file mode 100644 index b4f38a3bf1e..00000000000 --- a/chromium/services/service_manager/public/cpp/typemaps.gni +++ /dev/null @@ -1,6 +0,0 @@ -typemaps = [ - "//services/service_manager/public/cpp/bind_source_info.typemap", - "//services/service_manager/public/cpp/identity.typemap", - "//services/service_manager/public/cpp/interface_provider_spec.typemap", - "//services/service_manager/public/cpp/service_filter.typemap", -] diff --git a/chromium/services/service_manager/public/mojom/BUILD.gn b/chromium/services/service_manager/public/mojom/BUILD.gn index a9e2eca0163..b7f9ae5291b 100644 --- a/chromium/services/service_manager/public/mojom/BUILD.gn +++ b/chromium/services/service_manager/public/mojom/BUILD.gn @@ -23,6 +23,72 @@ mojom_component("mojom") { ":constants", "//mojo/public/mojom/base", ] + + cpp_typemaps = [ + { + types = [ + { + mojom = "service_manager.mojom.BindSourceInfo" + cpp = "::service_manager::BindSourceInfo" + }, + ] + traits_headers = [ + "//services/service_manager/public/cpp/bind_source_info_mojom_traits.h", + ] + traits_public_deps = [ + "//services/service_manager/public/cpp:cpp_types", + "//services/service_manager/public/cpp:mojom_traits", + ] + }, + { + types = [ + { + mojom = "service_manager.mojom.Identity" + cpp = "::service_manager::Identity" + }, + ] + traits_headers = + [ "//services/service_manager/public/cpp/identity_mojom_traits.h" ] + traits_public_deps = [ + "//services/service_manager/public/cpp:cpp_types", + "//services/service_manager/public/cpp:mojom_traits", + ] + }, + { + types = [ + { + mojom = "service_manager.mojom.InterfaceProviderSpec" + cpp = "::service_manager::InterfaceProviderSpec" + }, + { + mojom = "service_manager.mojom.InterfaceSet" + cpp = "::service_manager::InterfaceSet" + }, + { + mojom = "service_manager.mojom.CapabilitySet" + cpp = "::service_manager::CapabilitySet" + }, + ] + traits_headers = [ "//services/service_manager/public/cpp/interface_provider_spec_mojom_traits.h" ] + traits_public_deps = [ "//services/service_manager/public/cpp:cpp_types" ] + }, + { + types = [ + { + mojom = "service_manager.mojom.ServiceFilter" + cpp = "::service_manager::ServiceFilter" + }, + ] + + traits_headers = [ + "//services/service_manager/public/cpp/service_filter_mojom_traits.h", + ] + traits_public_deps = [ + "//services/service_manager/public/cpp:cpp_types", + "//services/service_manager/public/cpp:mojom_traits", + ] + }, + ] } mojom_component("constants") { diff --git a/chromium/services/service_manager/sandbox/BUILD.gn b/chromium/services/service_manager/sandbox/BUILD.gn deleted file mode 100644 index 21749ad3322..00000000000 --- a/chromium/services/service_manager/sandbox/BUILD.gn +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//build/buildflag_header.gni") -import("//build/config/chromecast_build.gni") -import("//build/config/sanitizers/sanitizers.gni") - -component("sandbox") { - sources = [ - "export.h", - "features.cc", - "features.h", - "sandbox.cc", - "sandbox.h", - "sandbox_delegate.h", - "sandbox_type.cc", - "sandbox_type.h", - "switches.cc", - "switches.h", - ] - defines = [ "SERVICE_MANAGER_SANDBOX_IMPL" ] - public_deps = [ "//services/service_manager/embedder:embedder_switches" ] - deps = [ - ":sanitizer_buildflags", - "//base", - "//sandbox:common", - ] - if (is_linux) { - sources += [ - "linux/bpf_audio_policy_linux.cc", - "linux/bpf_audio_policy_linux.h", - "linux/bpf_base_policy_linux.cc", - "linux/bpf_base_policy_linux.h", - "linux/bpf_broker_policy_linux.cc", - "linux/bpf_broker_policy_linux.h", - "linux/bpf_cdm_policy_linux.cc", - "linux/bpf_cdm_policy_linux.h", - "linux/bpf_cros_amd_gpu_policy_linux.cc", - "linux/bpf_cros_amd_gpu_policy_linux.h", - "linux/bpf_cros_arm_gpu_policy_linux.cc", - "linux/bpf_cros_arm_gpu_policy_linux.h", - "linux/bpf_gpu_policy_linux.cc", - "linux/bpf_gpu_policy_linux.h", - "linux/bpf_network_policy_linux.cc", - "linux/bpf_network_policy_linux.h", - "linux/bpf_ppapi_policy_linux.cc", - "linux/bpf_ppapi_policy_linux.h", - "linux/bpf_print_compositor_policy_linux.cc", - "linux/bpf_print_compositor_policy_linux.h", - "linux/bpf_renderer_policy_linux.cc", - "linux/bpf_renderer_policy_linux.h", - "linux/bpf_sharing_service_policy_linux.cc", - "linux/bpf_sharing_service_policy_linux.h", - "linux/bpf_speech_recognition_policy_linux.cc", - "linux/bpf_speech_recognition_policy_linux.h", - "linux/bpf_utility_policy_linux.cc", - "linux/bpf_utility_policy_linux.h", - "linux/sandbox_debug_handling_linux.cc", - "linux/sandbox_debug_handling_linux.h", - "linux/sandbox_linux.cc", - "linux/sandbox_linux.h", - "linux/sandbox_seccomp_bpf_linux.cc", - "linux/sandbox_seccomp_bpf_linux.h", - ] - configs += [ - "//media:media_config", - "//media/audio:platform_config", - ] - deps += [ - ":chromecast_sandbox_whitelist_buildflags", - "//sandbox:sandbox_buildflags", - "//sandbox/linux:sandbox_services", - "//sandbox/linux:seccomp_bpf", - "//sandbox/linux:suid_sandbox_client", - ] - } - if (is_chromeos) { - sources += [ - "linux/bpf_ime_policy_linux.cc", - "linux/bpf_ime_policy_linux.h", - "linux/bpf_tts_policy_linux.cc", - "linux/bpf_tts_policy_linux.h", - ] - } - if (is_mac) { - sources += [ - "mac/sandbox_mac.h", - "mac/sandbox_mac.mm", - ] - deps += [ "//sandbox/mac:seatbelt" ] - public_deps += [ "mac:packaged_sb_files" ] - libs = [ - "AppKit.framework", - "CoreFoundation.framework", - "CoreGraphics.framework", - "Foundation.framework", - "IOSurface.framework", - ] - } - if (is_win) { - sources += [ - "win/sandbox_diagnostics.cc", - "win/sandbox_diagnostics.h", - "win/sandbox_win.cc", - "win/sandbox_win.h", - ] - deps += [ "//sandbox/win:sandbox" ] - } - if (is_fuchsia) { - sources += [ - "fuchsia/sandbox_policy_fuchsia.cc", - "fuchsia/sandbox_policy_fuchsia.h", - ] - - public_deps += [ - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.io", - "//third_party/fuchsia-sdk/sdk/pkg/fidl", - "//third_party/fuchsia-sdk/sdk/pkg/zx", - ] - - deps += [ - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.fonts", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.logger", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.mediacodec", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.net", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.netstack", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem", - "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.scenic", - ] - } -} - -buildflag_header("sanitizer_buildflags") { - header = "sanitizer_buildflags.h" - flags = [ "USING_SANITIZER=$using_sanitizer" ] -} - -buildflag_header("chromecast_sandbox_whitelist_buildflags") { - header = "chromecast_sandbox_whitelist_buildflags.h" - flags = [ "ENABLE_CHROMECAST_GPU_SANDBOX_WHITELIST=$is_chromecast" ] -} diff --git a/chromium/services/service_manager/sandbox/DEPS b/chromium/services/service_manager/sandbox/DEPS deleted file mode 100644 index 804a308655f..00000000000 --- a/chromium/services/service_manager/sandbox/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -include_rules = [ - "+sandbox/constants.h", - "+sandbox", -] diff --git a/chromium/services/service_manager/sandbox/OWNERS b/chromium/services/service_manager/sandbox/OWNERS deleted file mode 100644 index e02183bdf41..00000000000 --- a/chromium/services/service_manager/sandbox/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -set noparent -file://sandbox/OWNERS -# COMPONENT: Internals>Sandbox -# TEAM: security-dev@chromium.org diff --git a/chromium/services/service_manager/sandbox/export.h b/chromium/services/service_manager/sandbox/export.h deleted file mode 100644 index 29fa29dfda1..00000000000 --- a/chromium/services/service_manager/sandbox/export.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_EXPORT_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_EXPORT_H_ - -#if defined(COMPONENT_BUILD) -#if defined(WIN32) - -#if defined(SERVICE_MANAGER_SANDBOX_IMPL) -#define SERVICE_MANAGER_SANDBOX_EXPORT __declspec(dllexport) -#else -#define SERVICE_MANAGER_SANDBOX_EXPORT __declspec(dllimport) -#endif // defined(SERVICE_MANAGER_SANDBOX_IMPL) - -#else // defined(WIN32) -#if defined(SERVICE_MANAGER_SANDBOX_IMPL) -#define SERVICE_MANAGER_SANDBOX_EXPORT __attribute__((visibility("default"))) -#else -#define SERVICE_MANAGER_SANDBOX_EXPORT -#endif // defined(SERVICE_MANAGER_SANDBOX_IMPL) -#endif - -#else // defined(COMPONENT_BUILD) -#define SERVICE_MANAGER_SANDBOX_EXPORT -#endif - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_EXPORT_H_ diff --git a/chromium/services/service_manager/sandbox/features.cc b/chromium/services/service_manager/sandbox/features.cc deleted file mode 100644 index c29946b6eb8..00000000000 --- a/chromium/services/service_manager/sandbox/features.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/features.h" - -#include "build/build_config.h" - -namespace service_manager { -namespace features { - -// Enables audio service sandbox. -// (Only causes an effect when feature kAudioServiceOutOfProcess is enabled.) -const base::Feature kAudioServiceSandbox { - "AudioServiceSandbox", -#if defined(OS_WIN) || defined(OS_MACOSX) - base::FEATURE_ENABLED_BY_DEFAULT -#else - base::FEATURE_DISABLED_BY_DEFAULT -#endif // defined(OS_WIN) || defined(OS_MACOSX) -}; - -#if !defined(OS_MACOSX) -// Enables network service sandbox. -// (Only causes an effect when feature kNetworkService is enabled.) -const base::Feature kNetworkServiceSandbox { - "NetworkServiceSandbox", - base::FEATURE_DISABLED_BY_DEFAULT -}; -#endif // !defined(OS_MACOSX) - -#if defined(OS_WIN) -// Emergency "off switch" for new Windows sandbox security mitigation, -// sandbox::MITIGATION_EXTENSION_POINT_DISABLE. -const base::Feature kWinSboxDisableExtensionPoints{ - "WinSboxDisableExtensionPoint", base::FEATURE_ENABLED_BY_DEFAULT}; - -// Enables GPU AppContainer sandbox on Windows. -const base::Feature kGpuAppContainer{"GpuAppContainer", - base::FEATURE_DISABLED_BY_DEFAULT}; - -// Enables GPU Low Privilege AppContainer when combined with kGpuAppContainer. -const base::Feature kGpuLPAC{"GpuLPAC", base::FEATURE_ENABLED_BY_DEFAULT}; -#endif // defined(OS_WIN) - -#if !defined(OS_ANDROID) -// Controls whether the isolated XR service is sandboxed. -const base::Feature kXRSandbox{"XRSandbox", base::FEATURE_ENABLED_BY_DEFAULT}; -#endif // !defined(OS_ANDROID) - -} // namespace features -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/features.h b/chromium/services/service_manager/sandbox/features.h deleted file mode 100644 index 86735e2330d..00000000000 --- a/chromium/services/service_manager/sandbox/features.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file defines all the public base::FeatureList features for the content -// module. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_FEATURES_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_FEATURES_H_ - -#include "base/feature_list.h" -#include "build/build_config.h" -#include "services/service_manager/sandbox/export.h" - -namespace service_manager { -namespace features { - -SERVICE_MANAGER_SANDBOX_EXPORT extern const base::Feature kAudioServiceSandbox; - -#if !defined(OS_MACOSX) -SERVICE_MANAGER_SANDBOX_EXPORT extern const base::Feature - kNetworkServiceSandbox; -#endif - -#if defined(OS_WIN) -SERVICE_MANAGER_SANDBOX_EXPORT extern const base::Feature - kWinSboxDisableExtensionPoints; -SERVICE_MANAGER_SANDBOX_EXPORT extern const base::Feature kGpuAppContainer; -SERVICE_MANAGER_SANDBOX_EXPORT extern const base::Feature kGpuLPAC; -#endif // defined(OS_WIN) - -#if !defined(OS_ANDROID) -SERVICE_MANAGER_SANDBOX_EXPORT extern const base::Feature kXRSandbox; -#endif // !defined(OS_ANDROID) - -} // namespace features -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_FEATURES_H_ diff --git a/chromium/services/service_manager/sandbox/fuchsia/OWNERS b/chromium/services/service_manager/sandbox/fuchsia/OWNERS deleted file mode 100644 index 7632a21d130..00000000000 --- a/chromium/services/service_manager/sandbox/fuchsia/OWNERS +++ /dev/null @@ -1 +0,0 @@ -file://fuchsia/SECURITY_OWNERS diff --git a/chromium/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc b/chromium/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc deleted file mode 100644 index 0d178b90d10..00000000000 --- a/chromium/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.cc +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.h" - -#include <lib/fdio/spawn.h> -#include <stdio.h> -#include <zircon/processargs.h> -#include <zircon/syscalls/policy.h> - -#include <fuchsia/camera3/cpp/fidl.h> -#include <fuchsia/fonts/cpp/fidl.h> -#include <fuchsia/intl/cpp/fidl.h> -#include <fuchsia/logger/cpp/fidl.h> -#include <fuchsia/mediacodec/cpp/fidl.h> -#include <fuchsia/net/cpp/fidl.h> -#include <fuchsia/netstack/cpp/fidl.h> -#include <fuchsia/sysmem/cpp/fidl.h> -#include <fuchsia/ui/scenic/cpp/fidl.h> -#include <lib/sys/cpp/component_context.h> -#include <lib/sys/cpp/service_directory.h> - -#include <memory> -#include <utility> - -#include "base/base_paths_fuchsia.h" -#include "base/command_line.h" -#include "base/containers/span.h" -#include "base/files/file_util.h" -#include "base/fuchsia/default_job.h" -#include "base/fuchsia/filtered_service_directory.h" -#include "base/fuchsia/fuchsia_logging.h" -#include "base/fuchsia/process_context.h" -#include "base/path_service.h" -#include "base/process/launch.h" -#include "base/process/process.h" -#include "base/threading/thread_task_runner_handle.h" -#include "services/service_manager/sandbox/switches.h" - -namespace service_manager { -namespace { - -enum SandboxFeature { - // Clones the job. This is required to start new processes (to make it useful - // the process will also need access to the fuchsia.process.Launcher service). - kCloneJob = 1 << 0, - - // Provides access to resources required by Vulkan. - kProvideVulkanResources = 1 << 1, - - // Read only access to /config/ssl, which contains root certs info. - kProvideSslConfig = 1 << 2, - - // Uses a service directory channel that is explicitly passed by the caller - // instead of automatically connecting to the service directory of the current - // process' namespace. Intended for use by SandboxType::kWebContext. - kUseServiceDirectoryOverride = 1 << 3, - - // Allows the process to use the ambient mark-vmo-as-executable capability. - kAmbientMarkVmoAsExecutable = 1 << 4, -}; - -struct SandboxConfig { - base::span<const char* const> services; - uint32_t features; -}; - -constexpr SandboxConfig kWebContextConfig = { - // Services directory is passed by calling SetServiceDirectory(). - base::span<const char* const>(), - - // Context processes only actually use the kUseServiceDirectoryOverride - // and kCloneJob |features| themselves. However, they must be granted - // all of the other features to delegate to child processes. - kCloneJob | kProvideVulkanResources | kProvideSslConfig | - kAmbientMarkVmoAsExecutable | kUseServiceDirectoryOverride, -}; - -constexpr SandboxConfig kGpuConfig = { - base::make_span((const char* const[]){ - fuchsia::sysmem::Allocator::Name_, - "fuchsia.vulkan.loader.Loader", - fuchsia::ui::scenic::Scenic::Name_, - }), - kProvideVulkanResources, -}; - -constexpr SandboxConfig kNetworkConfig = { - base::make_span((const char* const[]){ - fuchsia::net::NameLookup::Name_, - fuchsia::netstack::Netstack::Name_, - "fuchsia.posix.socket.Provider", - }), - kProvideSslConfig, -}; - -constexpr SandboxConfig kRendererConfig = { - base::make_span((const char* const[]){ - fuchsia::fonts::Provider::Name_, - fuchsia::mediacodec::CodecFactory::Name_, - fuchsia::sysmem::Allocator::Name_, - }), - kAmbientMarkVmoAsExecutable, -}; - -constexpr SandboxConfig kVideoCaptureConfig = { - base::make_span((const char* const[]){ - fuchsia::camera3::DeviceWatcher::Name_, - fuchsia::sysmem::Allocator::Name_, - }), - 0, -}; - -// No-access-to-anything. -constexpr SandboxConfig kEmptySandboxConfig = { - base::span<const char* const>(), - 0, -}; - -const SandboxConfig* GetConfigForSandboxType(SandboxType type) { - switch (type) { - case SandboxType::kNoSandbox: - return nullptr; - case SandboxType::kGpu: - return &kGpuConfig; - case SandboxType::kNetwork: - return &kNetworkConfig; - case SandboxType::kRenderer: - return &kRendererConfig; - case SandboxType::kWebContext: - return &kWebContextConfig; - case SandboxType::kVideoCapture: - return &kVideoCaptureConfig; - // Remaining types receive no-access-to-anything. - case SandboxType::kAudio: - case SandboxType::kCdm: - case SandboxType::kPpapi: - case SandboxType::kPrintCompositor: - case SandboxType::kSharingService: - case SandboxType::kSpeechRecognition: - case SandboxType::kUtility: - return &kEmptySandboxConfig; - } -} - -// Services that are passed to all processes. -constexpr base::span<const char* const> kDefaultServices = base::make_span( - (const char* const[]){fuchsia::intl::PropertyProvider::Name_, - fuchsia::logger::LogSink::Name_}); - -} // namespace - -SandboxPolicyFuchsia::SandboxPolicyFuchsia(service_manager::SandboxType type) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - service_manager::switches::kNoSandbox)) { - type_ = service_manager::SandboxType::kNoSandbox; - } else { - type_ = type; - } - // If we need to pass some services for the given sandbox type then create - // |sandbox_directory_| and initialize it with the corresponding list of - // services. FilteredServiceDirectory must be initialized on a thread that has - // async_dispatcher. - const SandboxConfig* config = GetConfigForSandboxType(type_); - if (config && !(config->features & kUseServiceDirectoryOverride)) { - service_directory_task_runner_ = base::ThreadTaskRunnerHandle::Get(); - service_directory_ = - std::make_unique<base::fuchsia::FilteredServiceDirectory>( - base::ComponentContextForProcess()->svc().get()); - for (const char* service_name : kDefaultServices) { - service_directory_->AddService(service_name); - } - for (const char* service_name : config->services) { - service_directory_->AddService(service_name); - } - // Bind the service directory and store the client channel for - // UpdateLaunchOptionsForSandbox()'s use. - service_directory_->ConnectClient(service_directory_client_.NewRequest()); - CHECK(service_directory_client_); - } -} - -SandboxPolicyFuchsia::~SandboxPolicyFuchsia() { - if (service_directory_) { - service_directory_task_runner_->DeleteSoon(FROM_HERE, - std::move(service_directory_)); - } -} - -void SandboxPolicyFuchsia::SetServiceDirectory( - fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory_client) { - DCHECK(GetConfigForSandboxType(type_)->features & - kUseServiceDirectoryOverride); - DCHECK(!service_directory_client_); - - service_directory_client_ = std::move(service_directory_client); -} - -void SandboxPolicyFuchsia::UpdateLaunchOptionsForSandbox( - base::LaunchOptions* options) { - - // Always clone stderr to get logs output. - options->fds_to_remap.push_back(std::make_pair(STDERR_FILENO, STDERR_FILENO)); - options->fds_to_remap.push_back(std::make_pair(STDOUT_FILENO, STDOUT_FILENO)); - - if (type_ == service_manager::SandboxType::kNoSandbox) { - options->spawn_flags = FDIO_SPAWN_CLONE_NAMESPACE | FDIO_SPAWN_CLONE_JOB; - options->clear_environment = false; - return; - } - - // Map /pkg (read-only files deployed from the package) into the child's - // namespace. - base::FilePath package_root; - base::PathService::Get(base::DIR_ASSETS, &package_root); - options->paths_to_clone.push_back(package_root); - - // If /config/data/tzdata/icu/ exists then it contains up-to-date timezone - // data which should be provided to all sub-processes, for consistency. - const auto kIcuTimezoneDataPath = base::FilePath("/config/data/tzdata/icu"); - static bool icu_timezone_data_exists = base::PathExists(kIcuTimezoneDataPath); - if (icu_timezone_data_exists) - options->paths_to_clone.push_back(kIcuTimezoneDataPath); - - // Clear environmental variables to better isolate the child from - // this process. - options->clear_environment = true; - - // Don't clone anything by default. - options->spawn_flags = 0; - - // Must get a config here as --no-sandbox bails out earlier. - const SandboxConfig* config = GetConfigForSandboxType(type_); - CHECK(config); - - if (config->features & kCloneJob) - options->spawn_flags |= FDIO_SPAWN_CLONE_JOB; - - if (config->features & kProvideSslConfig) - options->paths_to_clone.push_back(base::FilePath("/config/ssl")); - - if (config->features & kProvideVulkanResources) { - // /dev/class/gpu and /config/vulkan/icd.d are to used configure and - // access the GPU. - options->paths_to_clone.push_back(base::FilePath("/dev/class/gpu")); - const auto vulkan_icd_path = base::FilePath("/config/vulkan/icd.d"); - if (base::PathExists(vulkan_icd_path)) - options->paths_to_clone.push_back(vulkan_icd_path); - - // /dev/class/goldfish-pipe, /dev/class/goldfish-address-space and - // /dev/class/goldfish-control are used for Fuchsia Emulator. - options->paths_to_clone.insert( - options->paths_to_clone.end(), - {base::FilePath("/dev/class/goldfish-pipe"), - base::FilePath("/dev/class/goldfish-control"), - base::FilePath("/dev/class/goldfish-address-space")}); - } - - // If the process needs access to any services then transfer the - // |service_directory_client_| handle for it to mount at "/svc". - if (service_directory_client_) { - options->paths_to_transfer.push_back(base::PathToTransfer{ - base::FilePath("/svc"), - service_directory_client_.TakeChannel().release()}); - } - - // Isolate the child process from the call by launching it in its own job. - zx_status_t status = zx::job::create(*base::GetDefaultJob(), 0, &job_); - ZX_CHECK(status == ZX_OK, status) << "zx_job_create"; - options->job_handle = job_.get(); - - // Do not allow ambient VMO mark-as-executable capability to be inherited - // by processes that do not need to JIT (i.e. do not run V8/WASM). - if (!(config->features & kAmbientMarkVmoAsExecutable)) { - zx_policy_basic_v2_t deny_ambient_mark_vmo_exec{ - ZX_POL_AMBIENT_MARK_VMO_EXEC, ZX_POL_ACTION_KILL, ZX_POL_OVERRIDE_DENY}; - status = job_.set_policy(ZX_JOB_POL_RELATIVE, ZX_JOB_POL_BASIC_V2, - &deny_ambient_mark_vmo_exec, 1); - ZX_CHECK(status == ZX_OK, status) << "zx_job_set_policy"; - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.h b/chromium/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.h deleted file mode 100644 index 7811581aba7..00000000000 --- a/chromium/services/service_manager/sandbox/fuchsia/sandbox_policy_fuchsia.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_FUCHSIA_SANDBOX_POLICY_FUCHSIA_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_FUCHSIA_SANDBOX_POLICY_FUCHSIA_H_ - -#include <fuchsia/io/cpp/fidl.h> -#include <lib/fidl/cpp/interface_handle.h> -#include <lib/zx/job.h> - -#include "base/memory/ref_counted.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/sandbox_type.h" - -namespace base { -struct LaunchOptions; -class SequencedTaskRunner; - -namespace fuchsia { -class FilteredServiceDirectory; -} // namespace fuchsia - -} // namespace base - -namespace service_manager { - -class SERVICE_MANAGER_SANDBOX_EXPORT SandboxPolicyFuchsia { - public: - // Must be called on the IO thread. - explicit SandboxPolicyFuchsia(service_manager::SandboxType type); - ~SandboxPolicyFuchsia(); - - // Sets the service directory to pass to the child process when launching it. - // This is only supported for SandboxType::kWebContext processes. If this is - // not called for a WEB_CONTEXT process then it will receive no services. - void SetServiceDirectory( - fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory_client); - - // Modifies the process launch |options| to achieve the level of - // isolation appropriate for current the sandbox type. The caller may then add - // any descriptors or handles afterward to grant additional capabilities - // to the new process. - void UpdateLaunchOptionsForSandbox(base::LaunchOptions* options); - - private: - service_manager::SandboxType type_; - - // Services directory used for the /svc namespace of the child process. - std::unique_ptr<base::fuchsia::FilteredServiceDirectory> service_directory_; - fidl::InterfaceHandle<::fuchsia::io::Directory> service_directory_client_; - scoped_refptr<base::SequencedTaskRunner> service_directory_task_runner_; - - // Job in which the child process is launched. - zx::job job_; - - DISALLOW_COPY_AND_ASSIGN(SandboxPolicyFuchsia); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_FUCHSIA_SANDBOX_POLICY_FUCHSIA_H_ diff --git a/chromium/services/service_manager/sandbox/linux/OWNERS b/chromium/services/service_manager/sandbox/linux/OWNERS deleted file mode 100644 index 57df6dc7ec4..00000000000 --- a/chromium/services/service_manager/sandbox/linux/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -set noparent -file://sandbox/linux/OWNERS diff --git a/chromium/services/service_manager/sandbox/linux/bpf_audio_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_audio_policy_linux.cc deleted file mode 100644 index dca79767558..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_audio_policy_linux.cc +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_audio_policy_linux.h" - -#include <sys/socket.h> - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/services/syscall_wrappers.h" -#include "sandbox/linux/syscall_broker/broker_process.h" -#include "sandbox/linux/system_headers/linux_futex.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Arg; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::If; -using sandbox::bpf_dsl::ResultExpr; -using sandbox::bpf_dsl::Trap; -using sandbox::syscall_broker::BrokerProcess; - -namespace service_manager { - -AudioProcessPolicy::AudioProcessPolicy() = default; - -AudioProcessPolicy::~AudioProcessPolicy() = default; - -ResultExpr AudioProcessPolicy::EvaluateSyscall(int system_call_number) const { - switch (system_call_number) { -#if defined(__NR_connect) - case __NR_connect: -#endif -#if defined(__NR_ftruncate) - case __NR_ftruncate: -#endif -#if defined(__NR_ftruncate64) - case __NR_ftruncate64: -#endif -#if defined(__NR_fallocate) - case __NR_fallocate: -#endif -#if defined(__NR_getdents) - case __NR_getdents: -#endif -#if defined(__NR_getpeername) - case __NR_getpeername: -#endif -#if defined(__NR_getsockopt) - case __NR_getsockopt: -#endif -#if defined(__NR_getsockname) - case __NR_getsockname: -#endif -#if defined(__NR_ioctl) - case __NR_ioctl: -#endif -#if defined(__NR_memfd_create) - case __NR_memfd_create: -#endif -#if defined(__NR_pwrite) - case __NR_pwrite: -#endif -#if defined(__NR_pwrite64) - case __NR_pwrite64: -#endif -#if defined(__NR_sched_setscheduler) - case __NR_sched_setscheduler: -#endif -#if defined(__NR_setsockopt) - case __NR_setsockopt: -#endif -#if defined(__NR_uname) - case __NR_uname: -#endif - return Allow(); -#if defined(__NR_futex) - case __NR_futex: { - const Arg<int> op(1); -#if defined(USE_PULSEAUDIO) - return Switch(op & ~FUTEX_PRIVATE_FLAG) - .SANDBOX_BPF_DSL_CASES( - (FUTEX_CMP_REQUEUE, FUTEX_LOCK_PI, FUTEX_UNLOCK_PI, FUTEX_WAIT, - FUTEX_WAIT_BITSET, FUTEX_WAKE), - Allow()) - .Default(Error(EPERM)); -#else - return sandbox::RestrictFutex(); -#endif - } -#endif -#if defined(__NR_kill) - case __NR_kill: { - // man kill says: - // "If sig is 0, then no signal is sent, but existence and permission - // checks are still performed; this can be used to check for the - // existence of a process ID or process group ID that the caller is - // permitted to signal." - // - // This seems to be tripping up at least ESET's NOD32 anti-virus, causing - // an unnecessary crash in the audio process. See: http://crbug.com/904787 - const Arg<pid_t> pid(0); - const Arg<int> sig(1); - return If(pid == sandbox::sys_getpid(), Allow()) - .ElseIf(sig == 0, Error(EPERM)) - .Else(sandbox::CrashSIGSYSKill()); - } -#endif -#if defined(__NR_socket) - case __NR_socket: { - const Arg<int> domain(0); - return If(domain == AF_UNIX, Allow()).Else(Error(EPERM)); - } -#endif - default: -#if defined(__x86_64__) - if (sandbox::SyscallSets::IsSystemVSemaphores(system_call_number) || - sandbox::SyscallSets::IsSystemVSharedMemory(system_call_number)) { - return Allow(); - } -#elif defined(__i386__) - if (sandbox::SyscallSets::IsSystemVIpc(system_call_number)) - return Allow(); -#endif - - auto* broker_process = SandboxLinux::GetInstance()->broker_process(); - if (broker_process->IsSyscallAllowed(system_call_number)) - return Trap(BrokerProcess::SIGSYS_Handler, broker_process); - - return BPFBasePolicy::EvaluateSyscall(system_call_number); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_audio_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_audio_policy_linux.h deleted file mode 100644 index afbab1f9e8c..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_audio_policy_linux.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_AUDIO_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_AUDIO_POLICY_LINUX_H_ - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -class SERVICE_MANAGER_SANDBOX_EXPORT AudioProcessPolicy : public BPFBasePolicy { - public: - AudioProcessPolicy(); - ~AudioProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(AudioProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_AUDIO_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_base_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_base_policy_linux.cc deleted file mode 100644 index 687f648aa79..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_base_policy_linux.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -#include <errno.h> - -#include "base/check.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" - -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -namespace { - -// The errno used for denied file system access system calls, such as open(2). -static const int kFSDeniedErrno = EPERM; - -} // namespace. - -BPFBasePolicy::BPFBasePolicy() - : baseline_policy_(new sandbox::BaselinePolicy(kFSDeniedErrno)) {} -BPFBasePolicy::~BPFBasePolicy() {} - -ResultExpr BPFBasePolicy::EvaluateSyscall(int system_call_number) const { - DCHECK(baseline_policy_); - - // set_robust_list(2) is part of the futex(2) infrastructure. - // Chrome on Linux/Chrome OS will call set_robust_list(2) frequently. - // The baseline policy will EPERM set_robust_list(2), but on systems with - // SECCOMP logs enabled in auditd this will cause a ton of logspam. - // If we're not blocking the entire futex(2) infrastructure, we should allow - // set_robust_list(2) and quiet the logspam. - if (system_call_number == __NR_set_robust_list) { - return Allow(); - } - - return baseline_policy_->EvaluateSyscall(system_call_number); -} - -ResultExpr BPFBasePolicy::InvalidSyscall() const { - DCHECK(baseline_policy_); - return baseline_policy_->InvalidSyscall(); -} - -int BPFBasePolicy::GetFSDeniedErrno() { - return kFSDeniedErrno; -} - -} // namespace service_manager. diff --git a/chromium/services/service_manager/sandbox/linux/bpf_base_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_base_policy_linux.h deleted file mode 100644 index 8cae3526097..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_base_policy_linux.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_BASE_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_BASE_POLICY_LINUX_H_ - -#include <memory> - -#include "base/macros.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h" -#include "sandbox/linux/bpf_dsl/policy.h" -#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h" -#include "services/service_manager/sandbox/export.h" - -namespace service_manager { - -// The "baseline" BPF policy. Any other seccomp-bpf policy should inherit -// from it. -// It implements the main Policy interface. Due to its nature -// as a "kernel attack surface reduction" layer, it's implementation-defined. -class SERVICE_MANAGER_SANDBOX_EXPORT BPFBasePolicy - : public sandbox::bpf_dsl::Policy { - public: - BPFBasePolicy(); - ~BPFBasePolicy() override; - - // sandbox::bpf_dsl::Policy: - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - sandbox::bpf_dsl::ResultExpr InvalidSyscall() const override; - - // Get the errno(3) to return for filesystem errors. - static int GetFSDeniedErrno(); - - pid_t GetPolicyPid() const { return baseline_policy_->policy_pid(); } - - private: - // Compose the BaselinePolicy from sandbox/. - std::unique_ptr<sandbox::BaselinePolicy> baseline_policy_; - DISALLOW_COPY_AND_ASSIGN(BPFBasePolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_BASE_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc deleted file mode 100644 index 68af74e1fba..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_broker_policy_linux.cc +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_broker_policy_linux.h" - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" - -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -BrokerProcessPolicy::BrokerProcessPolicy( - const sandbox::syscall_broker::BrokerCommandSet& allowed_command_set) - : allowed_command_set_(allowed_command_set) {} - -BrokerProcessPolicy::~BrokerProcessPolicy() {} - -ResultExpr BrokerProcessPolicy::EvaluateSyscall(int sysno) const { - switch (sysno) { -#if defined(__NR_access) - case __NR_access: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_ACCESS)) - return Allow(); - break; -#endif -#if defined(__NR_faccessat) - case __NR_faccessat: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_ACCESS)) - return Allow(); - break; -#endif -#if defined(__NR_mkdir) - case __NR_mkdir: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_MKDIR)) - return Allow(); - break; -#endif -#if defined(__NR_mkdirat) - case __NR_mkdirat: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_MKDIR)) - return Allow(); - break; -#endif -#if defined(__NR_open) - case __NR_open: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_OPEN)) - return Allow(); - break; -#endif -#if defined(__NR_openat) - case __NR_openat: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_OPEN)) - return Allow(); - break; -#endif -#if defined(__NR_rename) - case __NR_rename: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_RENAME)) - return Allow(); - break; -#endif -#if defined(__NR_renameat) - case __NR_renameat: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_RENAME)) - return Allow(); - break; -#endif -#if defined(__NR_stat) - case __NR_stat: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_STAT)) - return Allow(); - break; -#endif -#if defined(__NR_stat64) - case __NR_stat64: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_STAT)) - return Allow(); - break; -#endif -#if defined(__NR_lstat) - case __NR_lstat: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_STAT)) - return Allow(); - break; -#endif -#if defined(__NR_lstat64) - case __NR_lstat64: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_STAT)) - return Allow(); - break; -#endif -#if defined(__NR_fstatat) - case __NR_fstatat: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_STAT)) - return Allow(); - break; -#endif -#if defined(__NR_newfstatat) - case __NR_newfstatat: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_STAT)) - return Allow(); - break; -#endif -#if defined(__NR_readlink) - case __NR_readlink: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_READLINK)) - return Allow(); - break; -#endif -#if defined(__NR_readlinkat) - case __NR_readlinkat: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_READLINK)) - return Allow(); - break; -#endif -#if defined(__NR_rmdir) - case __NR_rmdir: - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_RMDIR)) - return Allow(); - break; -#endif -#if defined(__NR_unlink) - case __NR_unlink: - // NOTE: Open() uses unlink() to make "temporary" files. - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_OPEN) || - allowed_command_set_.test(sandbox::syscall_broker::COMMAND_UNLINK)) { - return Allow(); - } - break; -#endif -#if defined(__NR_unlinkat) - case __NR_unlinkat: - // NOTE: Open() uses unlink() to make "temporary" files. - if (allowed_command_set_.test(sandbox::syscall_broker::COMMAND_OPEN) || - allowed_command_set_.test(sandbox::syscall_broker::COMMAND_UNLINK)) { - return Allow(); - } - break; -#endif - default: - break; - } - return BPFBasePolicy::EvaluateSyscall(sysno); -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_broker_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_broker_policy_linux.h deleted file mode 100644 index 8299a3a3b06..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_broker_policy_linux.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_BROKER_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_BROKER_POLICY_LINUX_H_ - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/syscall_broker/broker_command.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -// A broker policy is one for a privileged syscall broker that allows -// access, open, openat, and (in the non-Chrome OS case) unlink. -class SERVICE_MANAGER_SANDBOX_EXPORT BrokerProcessPolicy - : public BPFBasePolicy { - public: - explicit BrokerProcessPolicy( - const sandbox::syscall_broker::BrokerCommandSet& allowed_command_set); - ~BrokerProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - const sandbox::syscall_broker::BrokerCommandSet allowed_command_set_; - - DISALLOW_COPY_AND_ASSIGN(BrokerProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_BROKER_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_cdm_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_cdm_policy_linux.cc deleted file mode 100644 index 9d39e5d5de4..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_cdm_policy_linux.cc +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_cdm_policy_linux.h" - -#include <errno.h> - -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -using sandbox::SyscallSets; -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -CdmProcessPolicy::CdmProcessPolicy() {} -CdmProcessPolicy::~CdmProcessPolicy() {} - -ResultExpr CdmProcessPolicy::EvaluateSyscall(int sysno) const { - switch (sysno) { - case __NR_ioctl: - return sandbox::RestrictIoctl(); - // Allow the system calls below. - case __NR_fdatasync: - case __NR_fsync: - case __NR_ftruncate: - case __NR_fallocate: -#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ - defined(__aarch64__) - case __NR_getrlimit: -#endif -#if defined(__i386__) || defined(__arm__) - case __NR_ugetrlimit: -#endif - case __NR_mremap: // https://crbug.com/546204 - case __NR_pwrite64: - case __NR_sysinfo: - case __NR_times: - case __NR_uname: - return Allow(); - case __NR_sched_getaffinity: - return sandbox::RestrictSchedTarget(GetPolicyPid(), sysno); - default: - // Default on the content baseline policy. - return BPFBasePolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_cdm_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_cdm_policy_linux.h deleted file mode 100644 index ea07367a600..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_cdm_policy_linux.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_CDM_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_CDM_POLICY_LINUX_H_ - -#include "base/macros.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -// This policy can be used by the process hosting a Content Decryption Module. -class CdmProcessPolicy : public BPFBasePolicy { - public: - CdmProcessPolicy(); - ~CdmProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(CdmProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_CDM_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.cc deleted file mode 100644 index 56233eeace4..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.h" - -#include <errno.h> -#include <fcntl.h> -#include <linux/kcmp.h> -#include <sys/socket.h> - -// Some arch's (arm64 for instance) unistd.h don't pull in symbols used here -// unless these are defined. -#define __ARCH_WANT_SYSCALL_NO_AT -#define __ARCH_WANT_SYSCALL_DEPRECATED -#include <unistd.h> - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" - -using sandbox::bpf_dsl::AllOf; -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Arg; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::If; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -CrosAmdGpuProcessPolicy::CrosAmdGpuProcessPolicy() {} - -CrosAmdGpuProcessPolicy::~CrosAmdGpuProcessPolicy() {} - -ResultExpr CrosAmdGpuProcessPolicy::EvaluateSyscall(int sysno) const { - switch (sysno) { - case __NR_fstatfs: - case __NR_sched_setscheduler: - case __NR_sysinfo: - case __NR_uname: -#if !defined(__aarch64__) - case __NR_readlink: - case __NR_stat: -#endif - return Allow(); -#if defined(__x86_64__) - // Allow only AF_UNIX for |domain|. - case __NR_socket: - case __NR_socketpair: { - const Arg<int> domain(0); - return If(domain == AF_UNIX, Allow()).Else(Error(EPERM)); - } -#endif - case __NR_kcmp: { - const Arg<int> pid1(0); - const Arg<int> pid2(1); - const Arg<int> type(2); - const int policy_pid = GetPolicyPid(); - // Only allowed when comparing file handles for the calling thread. - return If(AllOf(pid1 == policy_pid, pid2 == policy_pid, - type == KCMP_FILE), - Allow()) - .Else(Error(EPERM)); - } - default: - // Default to the generic GPU policy. - return GpuProcessPolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.h deleted file mode 100644 index 23980dc67e2..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_CROS_AMD_GPU_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_CROS_AMD_GPU_POLICY_LINUX_H_ - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/bpf_gpu_policy_linux.h" - -namespace service_manager { - -// This policy is for AMD GPUs running on Chrome OS. -class SERVICE_MANAGER_SANDBOX_EXPORT CrosAmdGpuProcessPolicy - : public GpuProcessPolicy { - public: - CrosAmdGpuProcessPolicy(); - ~CrosAmdGpuProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(CrosAmdGpuProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_CROS_AMD_GPU_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_cros_arm_gpu_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_cros_arm_gpu_policy_linux.cc deleted file mode 100644 index cecd679785f..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_cros_arm_gpu_policy_linux.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_cros_arm_gpu_policy_linux.h" - -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" -#include "services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h" - -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Arg; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::If; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -CrosArmGpuProcessPolicy::CrosArmGpuProcessPolicy(bool allow_shmat) -#if defined(__arm__) || defined(__aarch64__) - : allow_shmat_(allow_shmat) -#endif -{ -} - -CrosArmGpuProcessPolicy::~CrosArmGpuProcessPolicy() {} - -ResultExpr CrosArmGpuProcessPolicy::EvaluateSyscall(int sysno) const { -#if defined(__arm__) || defined(__aarch64__) - if (allow_shmat_ && sysno == __NR_shmat) - return Allow(); -#endif // defined(__arm__) || defined(__aarch64__) - - switch (sysno) { -#if defined(__arm__) || defined(__aarch64__) - // ARM GPU sandbox is started earlier so we need to allow networking - // in the sandbox. - case __NR_connect: - case __NR_getpeername: - case __NR_getsockname: - case __NR_sysinfo: - case __NR_uname: - return Allow(); - // Allow only AF_UNIX for |domain|. - case __NR_socket: - case __NR_socketpair: { - const Arg<int> domain(0); - return If(domain == AF_UNIX, Allow()).Else(Error(EPERM)); - } -#endif // defined(__arm__) || defined(__aarch64__) - default: - // Default to the generic GPU policy. - return GpuProcessPolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_cros_arm_gpu_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_cros_arm_gpu_policy_linux.h deleted file mode 100644 index 36b8b9c9619..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_cros_arm_gpu_policy_linux.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_CROS_ARM_GPU_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_CROS_ARM_GPU_POLICY_LINUX_H_ - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/bpf_gpu_policy_linux.h" - -namespace service_manager { - -// This policy is for Chrome OS ARM. -class SERVICE_MANAGER_SANDBOX_EXPORT CrosArmGpuProcessPolicy - : public GpuProcessPolicy { - public: - explicit CrosArmGpuProcessPolicy(bool allow_shmat); - ~CrosArmGpuProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: -#if defined(__arm__) || defined(__aarch64__) - const bool allow_shmat_; // Allow shmat(2). -#endif - DISALLOW_COPY_AND_ASSIGN(CrosArmGpuProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_CROS_ARM_GPU_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc deleted file mode 100644 index d658059a294..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.cc +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_gpu_policy_linux.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/syscall_broker/broker_process.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" -#include "services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h" - -using sandbox::SyscallSets; -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::ResultExpr; -using sandbox::bpf_dsl::Trap; -using sandbox::syscall_broker::BrokerProcess; - -namespace service_manager { - -GpuProcessPolicy::GpuProcessPolicy() {} - -GpuProcessPolicy::~GpuProcessPolicy() {} - -// Main policy for x86_64/i386. Extended by CrosArmGpuProcessPolicy. -ResultExpr GpuProcessPolicy::EvaluateSyscall(int sysno) const { - switch (sysno) { -#if defined(OS_CHROMEOS) - case __NR_memfd_create: -#else // !defined(OS_CHROMEOS) - case __NR_fallocate: -#endif // defined(OS_CHROMEOS) - case __NR_ftruncate: -#if defined(__i386__) || defined(__arm__) || \ - (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)) - case __NR_ftruncate64: -#endif -#if !defined(__aarch64__) - case __NR_getdents: -#endif - case __NR_getdents64: - case __NR_ioctl: - return Allow(); -#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) - // The Nvidia driver uses flags not in the baseline policy - // (MAP_LOCKED | MAP_EXECUTABLE | MAP_32BIT) - case __NR_mmap: -#endif - // We also hit this on the linux_chromeos bot but don't yet know what - // weird flags were involved. - case __NR_mprotect: - // TODO(jln): restrict prctl. - case __NR_prctl: - case __NR_sysinfo: - case __NR_uname: // https://crbug.com/1075934 - return Allow(); - case __NR_sched_getaffinity: - case __NR_sched_setaffinity: - return sandbox::RestrictSchedTarget(GetPolicyPid(), sysno); - case __NR_prlimit64: - return sandbox::RestrictPrlimit64(GetPolicyPid()); - default: - if (SyscallSets::IsEventFd(sysno)) - return Allow(); - -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_X11) - if (SyscallSets::IsSystemVSharedMemory(sysno)) - return Allow(); -#endif - - auto* broker_process = SandboxLinux::GetInstance()->broker_process(); - if (broker_process->IsSyscallAllowed(sysno)) { - return Trap(BrokerProcess::SIGSYS_Handler, broker_process); - } - - // Default on the baseline policy. - return BPFBasePolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.h deleted file mode 100644 index c1091816681..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_gpu_policy_linux.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_GPU_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_GPU_POLICY_LINUX_H_ - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -class SERVICE_MANAGER_SANDBOX_EXPORT GpuProcessPolicy : public BPFBasePolicy { - public: - GpuProcessPolicy(); - ~GpuProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(GpuProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_GPU_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_ime_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_ime_policy_linux.cc deleted file mode 100644 index a3f79ee6370..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_ime_policy_linux.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_ime_policy_linux.h" - -#include <sys/socket.h> - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/syscall_broker/broker_process.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::ResultExpr; -using sandbox::bpf_dsl::Trap; -using sandbox::syscall_broker::BrokerProcess; - -namespace service_manager { - -ImeProcessPolicy::ImeProcessPolicy() {} - -ImeProcessPolicy::~ImeProcessPolicy() {} - -ResultExpr ImeProcessPolicy::EvaluateSyscall(int sysno) const { - switch (sysno) { -#if defined(__NR_uname) - case __NR_uname: -#endif -#if defined(__NR_clock_gettime) - case __NR_clock_gettime: -#endif - return Allow(); -// https://crbug.com/991435 -#if defined(__NR_getrusage) - case __NR_getrusage: - return sandbox::RestrictGetrusage(); -#endif - default: - auto* broker_process = SandboxLinux::GetInstance()->broker_process(); - if (broker_process->IsSyscallAllowed(sysno)) - return Trap(BrokerProcess::SIGSYS_Handler, broker_process); - - return BPFBasePolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_ime_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_ime_policy_linux.h deleted file mode 100644 index d9a8cd36522..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_ime_policy_linux.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_IME_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_IME_POLICY_LINUX_H_ - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -class SERVICE_MANAGER_SANDBOX_EXPORT ImeProcessPolicy : public BPFBasePolicy { - public: - ImeProcessPolicy(); - ~ImeProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall(int sysno) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(ImeProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_IME_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc deleted file mode 100644 index 201a6b371c0..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_network_policy_linux.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_network_policy_linux.h" - -#include <fcntl.h> -#include <unistd.h> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/syscall_broker/broker_file_permission.h" -#include "sandbox/linux/syscall_broker/broker_process.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" -#include "services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h" - -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::ResultExpr; -using sandbox::bpf_dsl::Trap; -using sandbox::syscall_broker::BrokerProcess; - -namespace service_manager { - -NetworkProcessPolicy::NetworkProcessPolicy() {} - -NetworkProcessPolicy::~NetworkProcessPolicy() {} - -ResultExpr NetworkProcessPolicy::EvaluateSyscall(int sysno) const { - auto* broker_process = SandboxLinux::GetInstance()->broker_process(); - if (broker_process->IsSyscallAllowed(sysno)) { - return Trap(BrokerProcess::SIGSYS_Handler, broker_process); - } - - // TODO(tsepez): FIX this. - return Allow(); -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_network_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_network_policy_linux.h deleted file mode 100644 index f5322058f05..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_network_policy_linux.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_NETWORK_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_NETWORK_POLICY_LINUX_H_ - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -class SERVICE_MANAGER_SANDBOX_EXPORT NetworkProcessPolicy - : public BPFBasePolicy { - public: - NetworkProcessPolicy(); - ~NetworkProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(NetworkProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_NETWORK_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_ppapi_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_ppapi_policy_linux.cc deleted file mode 100644 index 7e9c64ddb57..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_ppapi_policy_linux.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_ppapi_policy_linux.h" - -#include <errno.h> - -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -using sandbox::SyscallSets; -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -PpapiProcessPolicy::PpapiProcessPolicy() {} -PpapiProcessPolicy::~PpapiProcessPolicy() {} - -ResultExpr PpapiProcessPolicy::EvaluateSyscall(int sysno) const { - switch (sysno) { - // TODO(jln): restrict prctl. - case __NR_prctl: - case __NR_pwrite64: - case __NR_sched_get_priority_max: - case __NR_sched_get_priority_min: - case __NR_sysinfo: - case __NR_times: - return Allow(); - case __NR_sched_getaffinity: - case __NR_sched_getparam: - case __NR_sched_getscheduler: - case __NR_sched_setscheduler: - return sandbox::RestrictSchedTarget(GetPolicyPid(), sysno); - case __NR_ioctl: - return Error(ENOTTY); // Flash Access. - default: - // Default on the baseline policy. - return BPFBasePolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_ppapi_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_ppapi_policy_linux.h deleted file mode 100644 index 103b33e69e8..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_ppapi_policy_linux.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_PPAPI_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_PPAPI_POLICY_LINUX_H_ - -#include "base/macros.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -// Policy for Pepper plugins such as Flash. -class PpapiProcessPolicy : public BPFBasePolicy { - public: - PpapiProcessPolicy(); - ~PpapiProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(PpapiProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_PPAPI_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_print_compositor_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_print_compositor_policy_linux.cc deleted file mode 100644 index df978881f80..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_print_compositor_policy_linux.cc +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_print_compositor_policy_linux.h" - -#include <errno.h> - -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -using sandbox::SyscallSets; -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -PrintCompositorProcessPolicy::PrintCompositorProcessPolicy() {} -PrintCompositorProcessPolicy::~PrintCompositorProcessPolicy() {} - -ResultExpr PrintCompositorProcessPolicy::EvaluateSyscall(int sysno) const { - // TODO(weili): the current set of policy is exactly same as utility process - // policy. Check whether we can trim further. - switch (sysno) { - case __NR_ioctl: - return sandbox::RestrictIoctl(); - // Allow the system calls below. - case __NR_fdatasync: - case __NR_fsync: -#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ - defined(__aarch64__) - case __NR_getrlimit: -#endif -#if defined(__i386__) || defined(__arm__) - case __NR_ugetrlimit: -#endif - case __NR_mremap: // https://crbug.com/546204 - case __NR_pwrite64: - case __NR_sysinfo: - case __NR_times: - case __NR_uname: - return Allow(); - default: - // Default on the content baseline policy. - return BPFBasePolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_print_compositor_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_print_compositor_policy_linux.h deleted file mode 100644 index f6617c35027..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_print_compositor_policy_linux.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_PRINT_COMPOSITOR_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_PRINT_COMPOSITOR_POLICY_LINUX_H_ - -#include "base/macros.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -// This policy can be used by print compositor utility processes. -class PrintCompositorProcessPolicy : public BPFBasePolicy { - public: - PrintCompositorProcessPolicy(); - ~PrintCompositorProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(PrintCompositorProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_PRINT_COMPOSITOR_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_renderer_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_renderer_policy_linux.cc deleted file mode 100644 index a85c0ea8678..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_renderer_policy_linux.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_renderer_policy_linux.h" - -#include <errno.h> -#include <sys/ioctl.h> - -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -// TODO(vignatti): replace the local definitions below with #include -// <linux/dma-buf.h> once kernel version 4.6 becomes widely used. -#include <linux/types.h> - -struct local_dma_buf_sync { - __u64 flags; -}; -#define LOCAL_DMA_BUF_BASE 'b' -#define LOCAL_DMA_BUF_IOCTL_SYNC \ - _IOW(LOCAL_DMA_BUF_BASE, 0, struct local_dma_buf_sync) - -using sandbox::SyscallSets; -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Arg; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -namespace { - -ResultExpr RestrictIoctl() { - const Arg<unsigned long> request(1); - return Switch(request) - .SANDBOX_BPF_DSL_CASES((static_cast<unsigned long>(TCGETS), FIONREAD), - Allow()) - .SANDBOX_BPF_DSL_CASES( - (static_cast<unsigned long>(LOCAL_DMA_BUF_IOCTL_SYNC)), Allow()) - .Default(sandbox::CrashSIGSYSIoctl()); -} - -} // namespace - -RendererProcessPolicy::RendererProcessPolicy() {} -RendererProcessPolicy::~RendererProcessPolicy() {} - -ResultExpr RendererProcessPolicy::EvaluateSyscall(int sysno) const { - switch (sysno) { - // The baseline policy allows __NR_clock_gettime. Allow - // clock_getres() for V8. crbug.com/329053. - case __NR_clock_getres: - return sandbox::RestrictClockID(); - case __NR_ioctl: - return RestrictIoctl(); - // Allow the system calls below. - case __NR_fdatasync: - case __NR_fsync: - case __NR_ftruncate: -#if defined(__i386__) || defined(__arm__) || \ - (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)) - case __NR_ftruncate64: -#endif -#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ - defined(__aarch64__) - case __NR_getrlimit: - case __NR_setrlimit: -// We allow setrlimit to dynamically adjust the address space limit as -// needed for WebAssembly memory objects (https://crbug.com/750378). Even -// with setrlimit being allowed, we cannot raise rlim_max once it's -// lowered. Thus we generally have the same protection because we normally -// set rlim_max and rlim_cur together. -// -// See SandboxLinux::LimitAddressSpace() in -// services/service_manager/sandbox/linux/sandbox_linux.cc and -// ArrayBufferContents::ReserveMemory, -// ArrayBufferContents::ReleaseReservedMemory in -// third_party/WebKit/Source/platform/wtf/typed_arrays/ArrayBufferContents.cpp. -#endif -#if defined(__i386__) || defined(__arm__) - case __NR_ugetrlimit: -#endif - case __NR_mremap: // See crbug.com/149834. - case __NR_pwrite64: - case __NR_sched_get_priority_max: - case __NR_sched_get_priority_min: - case __NR_sysinfo: - case __NR_times: - case __NR_uname: - return Allow(); - case __NR_sched_getaffinity: - case __NR_sched_getparam: - case __NR_sched_getscheduler: - case __NR_sched_setscheduler: - return sandbox::RestrictSchedTarget(GetPolicyPid(), sysno); - case __NR_prlimit64: - // See crbug.com/662450 and setrlimit comment above. - return sandbox::RestrictPrlimit(GetPolicyPid()); - default: - // Default on the content baseline policy. - return BPFBasePolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_renderer_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_renderer_policy_linux.h deleted file mode 100644 index 86c514240d9..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_renderer_policy_linux.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_RENDERER_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_RENDERER_POLICY_LINUX_H_ - -#include "base/macros.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -// This policy can be used by both renderer and worker processes. -class RendererProcessPolicy : public BPFBasePolicy { - public: - RendererProcessPolicy(); - ~RendererProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(RendererProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_RENDERER_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_sharing_service_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_sharing_service_policy_linux.cc deleted file mode 100644 index 483c1c644a0..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_sharing_service_policy_linux.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_sharing_service_policy_linux.h" - -#include <errno.h> - -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -using sandbox::SyscallSets; -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -ResultExpr SharingServiceProcessPolicy::EvaluateSyscall(int sysno) const { - switch (sysno) { - case __NR_ioctl: - return sandbox::RestrictIoctl(); - // Allow the system calls below. -#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ - defined(__aarch64__) - case __NR_getrlimit: -#endif -#if defined(__i386__) || defined(__arm__) - case __NR_ugetrlimit: -#endif - case __NR_mremap: // https://crbug.com/546204 - case __NR_pwrite64: - case __NR_times: - return Allow(); - default: - // Default on the content baseline policy. - return BPFBasePolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_sharing_service_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_sharing_service_policy_linux.h deleted file mode 100644 index 4b62254012c..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_sharing_service_policy_linux.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_SHARING_SERVICE_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_SHARING_SERVICE_POLICY_LINUX_H_ - -#include "base/macros.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -// This policy can be used by the Sharing service to host WebRTC. -class SharingServiceProcessPolicy : public BPFBasePolicy { - public: - SharingServiceProcessPolicy() = default; - ~SharingServiceProcessPolicy() override = default; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - SharingServiceProcessPolicy(const SharingServiceProcessPolicy&) = delete; - SharingServiceProcessPolicy& operator=(const SharingServiceProcessPolicy&) = - delete; -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_UTILITY_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_speech_recognition_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_speech_recognition_policy_linux.cc deleted file mode 100644 index d1eb78d5be0..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_speech_recognition_policy_linux.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_speech_recognition_policy_linux.h" - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/syscall_broker/broker_process.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::ResultExpr; -using sandbox::bpf_dsl::Trap; -using sandbox::syscall_broker::BrokerProcess; - -namespace service_manager { - -SpeechRecognitionProcessPolicy::SpeechRecognitionProcessPolicy() = default; -SpeechRecognitionProcessPolicy::~SpeechRecognitionProcessPolicy() = default; - -ResultExpr SpeechRecognitionProcessPolicy::EvaluateSyscall( - int system_call_number) const { - switch (system_call_number) { -#if defined(__NR_eventfd2) - case __NR_eventfd2: - return Allow(); -#endif -#if defined(__NR_getdents64) - case __NR_getdents64: - return Allow(); -#endif -#if defined(__NR_getdents) - case __NR_getdents: - return Allow(); -#endif - default: - auto* broker_process = SandboxLinux::GetInstance()->broker_process(); - if (broker_process->IsSyscallAllowed(system_call_number)) - return Trap(BrokerProcess::SIGSYS_Handler, broker_process); - - // Default on the content baseline policy. - return BPFBasePolicy::EvaluateSyscall(system_call_number); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_speech_recognition_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_speech_recognition_policy_linux.h deleted file mode 100644 index b12bc82755c..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_speech_recognition_policy_linux.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_SPEECH_RECOGNITION_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_SPEECH_RECOGNITION_POLICY_LINUX_H_ - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -// The process policy for the sandboxed utility process that loads the Speech -// On-Device API (SODA). This policy allows the syscalls used by the libsoda.so -// binary to transcribe audio into text. -class SERVICE_MANAGER_SANDBOX_EXPORT SpeechRecognitionProcessPolicy - : public BPFBasePolicy { - public: - SpeechRecognitionProcessPolicy(); - ~SpeechRecognitionProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_SPEECH_RECOGNITION_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_tts_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_tts_policy_linux.cc deleted file mode 100644 index 812072395ec..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_tts_policy_linux.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_tts_policy_linux.h" - -#include <sys/socket.h> - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/syscall_broker/broker_process.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::ResultExpr; -using sandbox::bpf_dsl::Trap; -using sandbox::syscall_broker::BrokerProcess; - -namespace service_manager { - -TtsProcessPolicy::TtsProcessPolicy() {} - -TtsProcessPolicy::~TtsProcessPolicy() {} - -ResultExpr TtsProcessPolicy::EvaluateSyscall(int sysno) const { - auto* broker_process = SandboxLinux::GetInstance()->broker_process(); - if (broker_process->IsSyscallAllowed(sysno)) - return Trap(BrokerProcess::SIGSYS_Handler, broker_process); - - return BPFBasePolicy::EvaluateSyscall(sysno); -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_tts_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_tts_policy_linux.h deleted file mode 100644 index a562a68cfce..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_tts_policy_linux.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_TTS_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_TTS_POLICY_LINUX_H_ - -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -class SERVICE_MANAGER_SANDBOX_EXPORT TtsProcessPolicy : public BPFBasePolicy { - public: - TtsProcessPolicy(); - ~TtsProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall(int sysno) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(TtsProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_TTS_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/bpf_utility_policy_linux.cc b/chromium/services/service_manager/sandbox/linux/bpf_utility_policy_linux.cc deleted file mode 100644 index 192081eead3..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_utility_policy_linux.cc +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/bpf_utility_policy_linux.h" - -#include <errno.h> - -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -using sandbox::SyscallSets; -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::ResultExpr; - -namespace service_manager { - -UtilityProcessPolicy::UtilityProcessPolicy() {} -UtilityProcessPolicy::~UtilityProcessPolicy() {} - -ResultExpr UtilityProcessPolicy::EvaluateSyscall(int sysno) const { - switch (sysno) { - case __NR_ioctl: - return sandbox::RestrictIoctl(); - case __NR_prlimit64: - // Restrict prlimit() to reference only the calling process. - return sandbox::RestrictPrlimitToGetrlimit(GetPolicyPid()); - // Allow the system calls below. - case __NR_fdatasync: - case __NR_fsync: -#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \ - defined(__aarch64__) - case __NR_getrlimit: -#endif -#if defined(__i386__) || defined(__arm__) - case __NR_ugetrlimit: -#endif - case __NR_mremap: // https://crbug.com/546204 - case __NR_pwrite64: - case __NR_sysinfo: - case __NR_times: - case __NR_uname: - return Allow(); - default: - // Default on the content baseline policy. - return BPFBasePolicy::EvaluateSyscall(sysno); - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/bpf_utility_policy_linux.h b/chromium/services/service_manager/sandbox/linux/bpf_utility_policy_linux.h deleted file mode 100644 index 34fa171c3a1..00000000000 --- a/chromium/services/service_manager/sandbox/linux/bpf_utility_policy_linux.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_UTILITY_POLICY_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_UTILITY_POLICY_LINUX_H_ - -#include "base/macros.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" - -namespace service_manager { - -// This policy can be used by utility processes. -class UtilityProcessPolicy : public BPFBasePolicy { - public: - UtilityProcessPolicy(); - ~UtilityProcessPolicy() override; - - sandbox::bpf_dsl::ResultExpr EvaluateSyscall( - int system_call_number) const override; - - private: - DISALLOW_COPY_AND_ASSIGN(UtilityProcessPolicy); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_BPF_UTILITY_POLICY_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/sandbox_debug_handling_linux.cc b/chromium/services/service_manager/sandbox/linux/sandbox_debug_handling_linux.cc deleted file mode 100644 index f435ccb4714..00000000000 --- a/chromium/services/service_manager/sandbox/linux/sandbox_debug_handling_linux.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/sandbox_debug_handling_linux.h" - -#include <errno.h> -#include <signal.h> -#include <stddef.h> -#include <sys/prctl.h> -#include <unistd.h> - -#include "base/command_line.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/strings/safe_sprintf.h" -#include "services/service_manager/sandbox/switches.h" - -namespace service_manager { - -namespace { - -void DoChrootSignalHandler(int) { - const int old_errno = errno; - const char kFirstMessage[] = "Chroot signal handler called.\n"; - ignore_result(write(STDERR_FILENO, kFirstMessage, sizeof(kFirstMessage) - 1)); - - const int chroot_ret = chroot("/"); - - char kSecondMessage[100]; - const ssize_t printed = base::strings::SafeSPrintf( - kSecondMessage, "chroot() returned %d. Errno is %d.\n", chroot_ret, - errno); - if (printed > 0 && printed < static_cast<ssize_t>(sizeof(kSecondMessage))) { - ignore_result(write(STDERR_FILENO, kSecondMessage, printed)); - } - errno = old_errno; -} - -// This is a quick hack to allow testing sandbox crash reports in production -// binaries. -// This installs a signal handler for SIGUSR2 that performs a chroot(). -// In most of our BPF policies, it is a "watched" system call which will -// trigger a SIGSYS signal whose handler will crash. -// This has been added during the investigation of https://crbug.com/415842. -void InstallCrashTestHandler() { - struct sigaction act = {}; - act.sa_handler = DoChrootSignalHandler; - CHECK_EQ(0, sigemptyset(&act.sa_mask)); - act.sa_flags = 0; - - PCHECK(0 == sigaction(SIGUSR2, &act, NULL)); -} - -bool IsSandboxDebuggingEnabled() { - return base::CommandLine::ForCurrentProcess()->HasSwitch( - service_manager::switches::kAllowSandboxDebugging); -} - -} // namespace - -// static -bool SandboxDebugHandling::SetDumpableStatusAndHandlers() { - if (IsSandboxDebuggingEnabled()) { - // If sandbox debugging is allowed, install a handler for sandbox-related - // crash testing. - InstallCrashTestHandler(); - return true; - } - - if (prctl(PR_SET_DUMPABLE, 0) != 0) { - PLOG(ERROR) << "Failed to set non-dumpable flag"; - return false; - } - - return prctl(PR_GET_DUMPABLE) == 0; -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/sandbox_debug_handling_linux.h b/chromium/services/service_manager/sandbox/linux/sandbox_debug_handling_linux.h deleted file mode 100644 index 17018f2665c..00000000000 --- a/chromium/services/service_manager/sandbox/linux/sandbox_debug_handling_linux.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_SANDBOX_DEBUG_HANDLING_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_SANDBOX_DEBUG_HANDLING_LINUX_H_ - -#include "base/macros.h" -#include "services/service_manager/sandbox/export.h" - -namespace service_manager { - -class SERVICE_MANAGER_SANDBOX_EXPORT SandboxDebugHandling { - public: - // Depending on the command line, set the current process as - // non dumpable. Also set any signal handlers for sandbox - // debugging. - static bool SetDumpableStatusAndHandlers(); - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(SandboxDebugHandling); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_SANDBOX_DEBUG_HANDLING_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/sandbox_linux.cc b/chromium/services/service_manager/sandbox/linux/sandbox_linux.cc deleted file mode 100644 index 3848ab4916e..00000000000 --- a/chromium/services/service_manager/sandbox/linux/sandbox_linux.cc +++ /dev/null @@ -1,563 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/sandbox_linux.h" - -#include <dirent.h> -#include <fcntl.h> -#include <stdint.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> - -#include <limits> -#include <memory> -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/command_line.h" -#include "base/feature_list.h" -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/singleton.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/string_number_conversions.h" -#include "base/system/sys_info.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "sandbox/constants.h" -#include "sandbox/linux/services/credentials.h" -#include "sandbox/linux/services/libc_interceptor.h" -#include "sandbox/linux/services/namespace_sandbox.h" -#include "sandbox/linux/services/proc_util.h" -#include "sandbox/linux/services/resource_limits.h" -#include "sandbox/linux/services/thread_helpers.h" -#include "sandbox/linux/services/yama.h" -#include "sandbox/linux/suid/client/setuid_sandbox_client.h" -#include "sandbox/linux/syscall_broker/broker_command.h" -#include "sandbox/linux/syscall_broker/broker_process.h" -#include "sandbox/sandbox_buildflags.h" -#include "services/service_manager/sandbox/linux/bpf_broker_policy_linux.h" -#include "services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h" -#include "services/service_manager/sandbox/sandbox.h" -#include "services/service_manager/sandbox/sandbox_type.h" -#include "services/service_manager/sandbox/switches.h" - -#if BUILDFLAG(USING_SANITIZER) -#include <sanitizer/common_interface_defs.h> -#endif - -using sandbox::Yama; - -namespace service_manager { - -namespace { - -void LogSandboxStarted(const std::string& sandbox_name) { - const std::string process_type = - base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kProcessType); - const std::string activated_sandbox = - "Activated " + sandbox_name + - " sandbox for process type: " + process_type + "."; - VLOG(1) << activated_sandbox; -} - -bool IsRunningTSAN() { -#if defined(THREAD_SANITIZER) - return true; -#else - return false; -#endif -} - -// Get a file descriptor to /proc. Either duplicate |proc_fd| or try to open -// it by using the filesystem directly. -// TODO(jln): get rid of this ugly interface. -base::ScopedFD OpenProc(int proc_fd) { - int ret_proc_fd = -1; - if (proc_fd >= 0) { - // If a handle to /proc is available, use it. This allows to bypass file - // system restrictions. - ret_proc_fd = - HANDLE_EINTR(openat(proc_fd, ".", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); - } else { - // Otherwise, make an attempt to access the file system directly. - ret_proc_fd = HANDLE_EINTR( - openat(AT_FDCWD, "/proc/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)); - } - DCHECK_LE(0, ret_proc_fd); - return base::ScopedFD(ret_proc_fd); -} - -bool UpdateProcessTypeAndEnableSandbox( - SandboxLinux::PreSandboxHook broker_side_hook, - SandboxLinux::Options options, - sandbox::syscall_broker::BrokerCommandSet allowed_command_set) { - base::CommandLine::StringVector exec = - base::CommandLine::ForCurrentProcess()->GetArgs(); - base::CommandLine::Reset(); - base::CommandLine::Init(0, nullptr); - base::CommandLine::ForCurrentProcess()->InitFromArgv(exec); - - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - std::string new_process_type = - command_line->GetSwitchValueASCII(switches::kProcessType); - if (!new_process_type.empty()) { - new_process_type.append("-broker"); - } else { - new_process_type = "broker"; - } - - VLOG(3) << "UpdateProcessTypeAndEnableSandbox: Updating process type to " - << new_process_type; - command_line->AppendSwitchASCII(switches::kProcessType, new_process_type); - - if (broker_side_hook) - CHECK(std::move(broker_side_hook).Run(options)); - - return SandboxSeccompBPF::StartSandboxWithExternalPolicy( - std::make_unique<BrokerProcessPolicy>(allowed_command_set), - base::ScopedFD()); -} - -} // namespace - -SandboxLinux::SandboxLinux() - : proc_fd_(-1), - seccomp_bpf_started_(false), - sandbox_status_flags_(kInvalid), - pre_initialized_(false), - seccomp_bpf_supported_(false), - seccomp_bpf_with_tsync_supported_(false), - yama_is_enforcing_(false), - initialize_sandbox_ran_(false), - setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()), - broker_process_(nullptr) { - if (!setuid_sandbox_client_) { - LOG(FATAL) << "Failed to instantiate the setuid sandbox client."; - } -#if BUILDFLAG(USING_SANITIZER) - sanitizer_args_ = std::make_unique<__sanitizer_sandbox_arguments>(); - *sanitizer_args_ = {0}; -#endif -} - -SandboxLinux::~SandboxLinux() { - if (pre_initialized_) { - CHECK(initialize_sandbox_ran_); - } -} - -SandboxLinux* SandboxLinux::GetInstance() { - SandboxLinux* instance = base::Singleton<SandboxLinux>::get(); - CHECK(instance); - return instance; -} - -void SandboxLinux::PreinitializeSandbox() { - CHECK(!pre_initialized_); - seccomp_bpf_supported_ = false; -#if BUILDFLAG(USING_SANITIZER) - // Sanitizers need to open some resources before the sandbox is enabled. - // This should not fork, not launch threads, not open a directory. - __sanitizer_sandbox_on_notify(sanitizer_args()); - sanitizer_args_.reset(); -#endif - - // Open proc_fd_. It would break the security of the setuid sandbox if it was - // not closed. - // If SandboxLinux::PreinitializeSandbox() runs, InitializeSandbox() must run - // as well. - proc_fd_ = HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC)); - CHECK_GE(proc_fd_, 0); - // We "pre-warm" the code that detects supports for seccomp BPF. - if (SandboxSeccompBPF::IsSeccompBPFDesired()) { - if (!SandboxSeccompBPF::SupportsSandbox()) { - VLOG(1) << "Lacking support for seccomp-bpf sandbox."; - } else { - seccomp_bpf_supported_ = true; - } - - if (SandboxSeccompBPF::SupportsSandboxWithTsync()) { - seccomp_bpf_with_tsync_supported_ = true; - } - } - - // Yama is a "global", system-level status. We assume it will not regress - // after startup. - const int yama_status = Yama::GetStatus(); - yama_is_enforcing_ = (yama_status & Yama::STATUS_PRESENT) && - (yama_status & Yama::STATUS_ENFORCING); - pre_initialized_ = true; -} - -void SandboxLinux::EngageNamespaceSandbox(bool from_zygote) { - CHECK(EngageNamespaceSandboxInternal(from_zygote)); -} - -bool SandboxLinux::EngageNamespaceSandboxIfPossible() { - return EngageNamespaceSandboxInternal(false /* from_zygote */); -} - -std::vector<int> SandboxLinux::GetFileDescriptorsToClose() { - std::vector<int> fds; - if (proc_fd_ >= 0) { - fds.push_back(proc_fd_); - } - return fds; -} - -int SandboxLinux::GetStatus() { - if (!pre_initialized_) { - return 0; - } - if (sandbox_status_flags_ == kInvalid) { - // Initialize sandbox_status_flags_. - sandbox_status_flags_ = 0; - if (setuid_sandbox_client_->IsSandboxed()) { - sandbox_status_flags_ |= kSUID; - if (setuid_sandbox_client_->IsInNewPIDNamespace()) - sandbox_status_flags_ |= kPIDNS; - if (setuid_sandbox_client_->IsInNewNETNamespace()) - sandbox_status_flags_ |= kNetNS; - } else if (sandbox::NamespaceSandbox::InNewUserNamespace()) { - sandbox_status_flags_ |= kUserNS; - if (sandbox::NamespaceSandbox::InNewPidNamespace()) - sandbox_status_flags_ |= kPIDNS; - if (sandbox::NamespaceSandbox::InNewNetNamespace()) - sandbox_status_flags_ |= kNetNS; - } - - // We report whether the sandbox will be activated when renderers, workers - // and PPAPI plugins go through sandbox initialization. - if (seccomp_bpf_supported()) { - sandbox_status_flags_ |= kSeccompBPF; - } - - if (seccomp_bpf_with_tsync_supported()) { - sandbox_status_flags_ |= kSeccompTSYNC; - } - - if (yama_is_enforcing_) { - sandbox_status_flags_ |= kYama; - } - } - - return sandbox_status_flags_; -} - -// Threads are counted via /proc/self/task. This is a little hairy because of -// PID namespaces and existing sandboxes, so "self" must really be used instead -// of using the pid. -bool SandboxLinux::IsSingleThreaded() const { - base::ScopedFD proc_fd(OpenProc(proc_fd_)); - - CHECK(proc_fd.is_valid()) << "Could not count threads, the sandbox was not " - << "pre-initialized properly."; - - const bool is_single_threaded = - sandbox::ThreadHelpers::IsSingleThreaded(proc_fd.get()); - - return is_single_threaded; -} - -bool SandboxLinux::seccomp_bpf_started() const { - return seccomp_bpf_started_; -} - -sandbox::SetuidSandboxClient* SandboxLinux::setuid_sandbox_client() const { - return setuid_sandbox_client_.get(); -} - -// For seccomp-bpf, we use the SandboxSeccompBPF class. -bool SandboxLinux::StartSeccompBPF(SandboxType sandbox_type, - PreSandboxHook hook, - const Options& options) { - CHECK(!seccomp_bpf_started_); - CHECK(pre_initialized_); -#if BUILDFLAG(USE_SECCOMP_BPF) - if (!seccomp_bpf_supported()) - return false; - - if (IsUnsandboxedSandboxType(sandbox_type) || - !SandboxSeccompBPF::IsSeccompBPFDesired() || - !SandboxSeccompBPF::SupportsSandbox()) { - return true; - } - - if (hook) - CHECK(std::move(hook).Run(options)); - - // If we allow threads *and* have multiple threads, try to use TSYNC. - sandbox::SandboxBPF::SeccompLevel seccomp_level = - options.allow_threads_during_sandbox_init && !IsSingleThreaded() - ? sandbox::SandboxBPF::SeccompLevel::MULTI_THREADED - : sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED; - - // If the kernel supports the sandbox, and if the command line says we - // should enable it, enable it or die. - std::unique_ptr<BPFBasePolicy> policy = - SandboxSeccompBPF::PolicyForSandboxType(sandbox_type, options); - SandboxSeccompBPF::StartSandboxWithExternalPolicy( - std::move(policy), OpenProc(proc_fd_), seccomp_level); - SandboxSeccompBPF::RunSandboxSanityChecks(sandbox_type, options); - seccomp_bpf_started_ = true; - LogSandboxStarted("seccomp-bpf"); - return true; -#else - return false; -#endif -} - -bool SandboxLinux::InitializeSandbox(SandboxType sandbox_type, - SandboxLinux::PreSandboxHook hook, - const Options& options) { - DCHECK(!initialize_sandbox_ran_); - initialize_sandbox_ran_ = true; - - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - const std::string process_type = - command_line->GetSwitchValueASCII(switches::kProcessType); - - // We need to make absolutely sure that our sandbox is "sealed" before - // returning. - // Unretained() since the current object is a Singleton. - base::ScopedClosureRunner sandbox_sealer( - base::BindOnce(&SandboxLinux::SealSandbox, base::Unretained(this))); - // Make sure that this function enables sandboxes as promised by GetStatus(). - // Unretained() since the current object is a Singleton. - base::ScopedClosureRunner sandbox_promise_keeper( - base::BindOnce(&SandboxLinux::CheckForBrokenPromises, - base::Unretained(this), sandbox_type)); - - const bool has_threads = !IsSingleThreaded(); - - // For now, restrict the |options.allow_threads_during_sandbox_init| option to - // the GPU process - DCHECK(process_type == switches::kGpuProcess || - !options.allow_threads_during_sandbox_init); - if (has_threads && !options.allow_threads_during_sandbox_init) { - std::string error_message = - "InitializeSandbox() called with multiple threads in process " + - process_type + "."; - // TSAN starts a helper thread, so we don't start the sandbox and don't - // even report an error about it. - if (IsRunningTSAN()) - return false; - - // The GPU process is allowed to call InitializeSandbox() with threads. - bool sandbox_failure_fatal = process_type != switches::kGpuProcess; - // This can be disabled with the '--gpu-sandbox-failures-fatal' flag. - // Setting the flag with no value or any value different than 'yes' or 'no' - // is equal to setting '--gpu-sandbox-failures-fatal=yes'. - if (process_type == switches::kGpuProcess && - command_line->HasSwitch(switches::kGpuSandboxFailuresFatal)) { - const std::string switch_value = - command_line->GetSwitchValueASCII(switches::kGpuSandboxFailuresFatal); - sandbox_failure_fatal = switch_value != "no"; - } - - if (sandbox_failure_fatal) { - error_message += " Try waiting for /proc to be updated."; - LOG(ERROR) << error_message; - // This will return if /proc/self eventually reports this process is - // single-threaded, or crash if it does not after a number of retries. - sandbox::ThreadHelpers::AssertSingleThreaded(); - } else { - LOG(ERROR) << error_message; - return false; - } - } - - // At this point we are either single threaded, or we won't be engaging the - // semantic layer of the sandbox and we won't care if there are file - // descriptors left open. - - // Pre-initialize if not already done. - if (!pre_initialized_) - PreinitializeSandbox(); - - // Turn on the namespace sandbox if our caller wants it (and the zygote hasn't - // done so already). - if (options.engage_namespace_sandbox) - EngageNamespaceSandbox(false /* from_zygote */); - - // Check for open directories, which can break the semantic sandbox layer. In - // some cases the caller doesn't want to enable the semantic sandbox layer, - // and this CHECK should be skipped. In this case, the caller should unset - // |options.check_for_open_directories|. - CHECK(!options.check_for_open_directories || !HasOpenDirectories()) - << "InitializeSandbox() called after unexpected directories have been " - << "opened. This breaks the security of the setuid sandbox."; - - sandbox::InitLibcLocaltimeFunctions(); - - // Attempt to limit the future size of the address space of the process. - // Fine to call with multiple threads as we don't use RLIMIT_STACK. - int error = 0; - const bool limited_as = LimitAddressSpace(&error); - if (error) { - // Restore errno. Internally to |LimitAddressSpace|, the errno due to - // setrlimit may be lost. - errno = error; - PCHECK(limited_as); - } - - return StartSeccompBPF(sandbox_type, std::move(hook), options); -} - -void SandboxLinux::StopThread(base::Thread* thread) { - DCHECK(thread); - StopThreadAndEnsureNotCounted(thread); -} - -bool SandboxLinux::seccomp_bpf_supported() const { - CHECK(pre_initialized_); - return seccomp_bpf_supported_; -} - -bool SandboxLinux::seccomp_bpf_with_tsync_supported() const { - CHECK(pre_initialized_); - return seccomp_bpf_with_tsync_supported_; -} - -rlim_t GetProcessDataSizeLimit(SandboxType sandbox_type) { -#if defined(ARCH_CPU_64_BITS) - if (sandbox_type == SandboxType::kGpu || - sandbox_type == SandboxType::kRenderer) { - // Allow the GPU/RENDERER process's sandbox to access more physical memory - // if it's available on the system. - constexpr rlim_t GB = 1024 * 1024 * 1024; - const rlim_t physical_memory = base::SysInfo::AmountOfPhysicalMemory(); - if (physical_memory > 16 * GB) { - return 16 * GB; - } else if (physical_memory > 8 * GB) { - return 8 * GB; - } - } -#endif - - return static_cast<rlim_t>(sandbox::kDataSizeLimit); -} - -bool SandboxLinux::LimitAddressSpace(int* error) { -#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ - !defined(THREAD_SANITIZER) && !defined(LEAK_SANITIZER) - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - SandboxType sandbox_type = SandboxTypeFromCommandLine(*command_line); - if (sandbox_type == SandboxType::kNoSandbox) { - return false; - } - - // Unfortunately, it does not appear possible to set RLIMIT_AS such that it - // will both (a) be high enough to support V8's and WebAssembly's address - // space requirements while also (b) being low enough to mitigate exploits - // using integer overflows that require large allocations, heap spray, or - // other memory-hungry attack modes. - - rlim_t process_data_size_limit = GetProcessDataSizeLimit(sandbox_type); - // Fine to call with multiple threads as we don't use RLIMIT_STACK. - *error = sandbox::ResourceLimits::Lower(RLIMIT_DATA, process_data_size_limit); - - // Cache the resource limit before turning on the sandbox. - base::SysInfo::AmountOfVirtualMemory(); - - return *error == 0; -#else - base::SysInfo::AmountOfVirtualMemory(); - return false; -#endif // !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && - // !defined(THREAD_SANITIZER) && !defined(LEAK_SANITIZER) -} - -void SandboxLinux::StartBrokerProcess( - const sandbox::syscall_broker::BrokerCommandSet& allowed_command_set, - std::vector<sandbox::syscall_broker::BrokerFilePermission> permissions, - PreSandboxHook broker_side_hook, - const Options& options) { - // Leaked at shutdown, so use bare |new|. - broker_process_ = new sandbox::syscall_broker::BrokerProcess( - BPFBasePolicy::GetFSDeniedErrno(), allowed_command_set, permissions); - - // The initialization callback will perform generic initialization and then - // call broker_sandboxer_callback. - CHECK(broker_process_->Init(base::BindOnce(&UpdateProcessTypeAndEnableSandbox, - std::move(broker_side_hook), - options, allowed_command_set))); -} - -bool SandboxLinux::HasOpenDirectories() const { - return sandbox::ProcUtil::HasOpenDirectory(proc_fd_); -} - -void SandboxLinux::SealSandbox() { - if (proc_fd_ >= 0) { - int ret = IGNORE_EINTR(close(proc_fd_)); - CHECK_EQ(0, ret); - proc_fd_ = -1; - } -} - -void SandboxLinux::CheckForBrokenPromises(SandboxType sandbox_type) { - if (sandbox_type != SandboxType::kRenderer && - sandbox_type != SandboxType::kPpapi) { - return; - } - // Make sure that any promise made with GetStatus() wasn't broken. - bool promised_seccomp_bpf_would_start = - (sandbox_status_flags_ != kInvalid) && (GetStatus() & kSeccompBPF); - CHECK(!promised_seccomp_bpf_would_start || seccomp_bpf_started_); -} - -void SandboxLinux::StopThreadAndEnsureNotCounted(base::Thread* thread) const { - DCHECK(thread); - base::ScopedFD proc_fd(OpenProc(proc_fd_)); - PCHECK(proc_fd.is_valid()); - CHECK( - sandbox::ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.get(), thread)); -} - -bool SandboxLinux::EngageNamespaceSandboxInternal(bool from_zygote) { - CHECK(pre_initialized_); - CHECK(IsSingleThreaded()) - << "The process cannot have multiple threads when engaging the namespace " - "sandbox, because the thread engaging the sandbox cannot ensure that " - "other threads close all their open directories."; - - if (from_zygote) { - // Check being in a new PID namespace created by the namespace sandbox and - // being the init process. - CHECK(sandbox::NamespaceSandbox::InNewPidNamespace()); - const pid_t pid = getpid(); - CHECK_EQ(1, pid); - } - - // After we successfully move to a new user ns, we don't allow this function - // to fail. - if (!sandbox::Credentials::MoveToNewUserNS()) { - return false; - } - - // Note: this requires SealSandbox() to be called later in this process to be - // safe, as this class is keeping a file descriptor to /proc/. - CHECK(sandbox::Credentials::DropFileSystemAccess(proc_fd_)); - - // Now we drop all capabilities that we can. In the zygote process, we need - // to keep CAP_SYS_ADMIN, to place each child in its own PID namespace - // later on. - std::vector<sandbox::Credentials::Capability> caps; - if (from_zygote) { - caps.push_back(sandbox::Credentials::Capability::SYS_ADMIN); - } - CHECK(sandbox::Credentials::SetCapabilities(proc_fd_, caps)); - return true; -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/sandbox_linux.h b/chromium/services/service_manager/sandbox/linux/sandbox_linux.h deleted file mode 100644 index 6a17f9edb63..00000000000 --- a/chromium/services/service_manager/sandbox/linux/sandbox_linux.h +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_SANDBOX_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_SANDBOX_LINUX_H_ - -#include <memory> -#include <string> -#include <vector> - -#include "base/check_op.h" -#include "base/macros.h" -#include "base/posix/global_descriptors.h" -#include "sandbox/linux/syscall_broker/broker_command.h" -#include "sandbox/linux/syscall_broker/broker_file_permission.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h" -#include "services/service_manager/sandbox/sandbox_type.h" -#include "services/service_manager/sandbox/sanitizer_buildflags.h" - -#if BUILDFLAG(USING_SANITIZER) -#include <sanitizer/common_interface_defs.h> -#endif - -namespace base { -template <typename T> -struct DefaultSingletonTraits; -class Thread; -} // namespace base - -namespace sandbox { -namespace syscall_broker { -class BrokerProcess; -} // namespace syscall_broker -class SetuidSandboxClient; -} // namespace sandbox - -namespace service_manager { - -// A singleton class to represent and change our sandboxing state for the -// three main Linux sandboxes. -// The sandboxing model allows using two layers of sandboxing. The first layer -// can be implemented either with unprivileged namespaces or with the setuid -// sandbox. This class provides a way to engage the namespace sandbox, but does -// not deal with the legacy setuid sandbox directly. -// The second layer is mainly based on seccomp-bpf and is engaged with -// InitializeSandbox(). InitializeSandbox() is also responsible for "sealing" -// the first layer of sandboxing. That is, InitializeSandbox must always be -// called to have any meaningful sandboxing at all. -class SERVICE_MANAGER_SANDBOX_EXPORT SandboxLinux { - public: - // This is a list of sandbox IPC methods which the renderer may send to the - // sandbox host. See - // https://chromium.googlesource.com/chromium/src/+/master/docs/linux/sandbox_ipc.md - // This isn't the full list, values < 32 are reserved for methods called from - // Skia, and values < 64 are reserved for libc_interceptor.cc. - enum LinuxSandboxIPCMethods { - DEPRECATED_METHOD_GET_FALLBACK_FONT_FOR_CHAR = 64, - DEPRECATED_METHOD_GET_CHILD_WITH_INODE, - DEPRECATED_METHOD_GET_STYLE_FOR_STRIKE, - METHOD_MAKE_SHARED_MEMORY_SEGMENT, - DEPRECATED_METHOD_MATCH_WITH_FALLBACK, - }; - - // These form a bitmask which describes the conditions of the Linux sandbox. - // Note: this doesn't strictly give you the current status, it states - // what will be enabled when the relevant processes are initialized. - enum Status { - // SUID sandbox active. - kSUID = 1 << 0, - - // Sandbox is using a new PID namespace. - kPIDNS = 1 << 1, - - // Sandbox is using a new network namespace. - kNetNS = 1 << 2, - - // seccomp-bpf sandbox active. - kSeccompBPF = 1 << 3, - - // The Yama LSM module is present and enforcing. - kYama = 1 << 4, - - // seccomp-bpf sandbox is active and the kernel supports TSYNC. - kSeccompTSYNC = 1 << 5, - - // User namespace sandbox active. - kUserNS = 1 << 6, - - // A flag that denotes an invalid sandbox status. - kInvalid = 1 << 31, - }; - - // SandboxLinux Options are a superset of SandboxSecompBPF Options. - struct Options : public SandboxSeccompBPF::Options { - // When running with a zygote, the namespace sandbox will have already - // been engaged prior to initializing SandboxLinux itself, and need not - // be done so again. Set to true to indicate that there isn't a zygote - // for this process and the step is to be performed here explicitly. - bool engage_namespace_sandbox = false; - - // Allow starting the sandbox with multiple threads already running. This - // will enable TSYNC for seccomp-BPF, which syncs the seccomp-BPF policy - // across all running threads. - bool allow_threads_during_sandbox_init = false; - - // Enables the CHECK for open directories. The open directory check is only - // useful for the chroot jail (from the semantic layer of the sandbox), and - // can safely be disabled if we are only enabling the seccomp-BPF layer. - bool check_for_open_directories = true; - }; - - // Callers can provide this hook to run code right before the policy - // is passed to the BPF compiler and the sandbox is engaged. If - // pre_sandbox_hook() returns true, the sandbox will be engaged - // afterwards, otherwise the process is terminated. - using PreSandboxHook = base::OnceCallback<bool(Options)>; - - // Get our singleton instance. - static SandboxLinux* GetInstance(); - - // Do some initialization that can only be done before any of the sandboxes - // are enabled. If using the setuid sandbox, this should be called manually - // before the setuid sandbox is engaged. - // Security: When this runs, it is imperative that either InitializeSandbox() - // runs as well or that all file descriptors returned in - // GetFileDescriptorsToClose() get closed. - // Otherwise file descriptors that bypass the security of the setuid sandbox - // would be kept open. One must be particularly careful if a process performs - // a fork(). - void PreinitializeSandbox(); - - // Check that the current process is the init process of a new PID - // namespace and then proceed to drop access to the file system by using - // a new unprivileged namespace. This is a layer-1 sandbox. - // In order for this sandbox to be effective, it must be "sealed" by calling - // InitializeSandbox(). - // Terminates the process in case the sandboxing operations cannot complete - // successfully. - void EngageNamespaceSandbox(bool from_zygote); - - // Performs the same actions as EngageNamespaceSandbox, but is allowed to - // to fail. This is useful when sandboxed non-renderer processes could - // benefit from extra sandboxing but is not strictly required on systems that - // don't support unprivileged user namespaces. - // Zygote should use EngageNamespaceSandbox instead. - bool EngageNamespaceSandboxIfPossible(); - - // Return a list of file descriptors to close if PreinitializeSandbox() ran - // but InitializeSandbox() won't. Avoid using. - // TODO(jln): get rid of this hack. - std::vector<int> GetFileDescriptorsToClose(); - - // Seal an eventual layer-1 sandbox and initialize the layer-2 sandbox with - // an adequate policy depending on the process type and command line - // arguments. - // Currently the layer-2 sandbox is composed of seccomp-bpf and address space - // limitations. - // This function should only be called without any thread running. - bool InitializeSandbox(SandboxType sandbox_type, - PreSandboxHook hook, - const Options& options); - - // Stop |thread| in a way that can be trusted by the sandbox. - void StopThread(base::Thread* thread); - - // Returns the status of the renderer, worker and ppapi sandbox. Can only - // be queried after going through PreinitializeSandbox(). This is a bitmask - // and uses the constants defined in "enum Status" above. Since the - // status needs to be provided before the sandboxes are actually started, - // this returns what will actually happen once InitializeSandbox() - // is called from inside these processes. - int GetStatus(); - - // Returns true if the current process is single-threaded or if the number - // of threads cannot be determined. - bool IsSingleThreaded() const; - - // Returns true if we started Seccomp BPF. - bool seccomp_bpf_started() const; - - // Simple accessor for our instance of the setuid sandbox. Will never return - // NULL. - // There is no StartSetuidSandbox(), the SetuidSandboxClient instance should - // be used directly. - sandbox::SetuidSandboxClient* setuid_sandbox_client() const; - - // Check the policy and eventually start the seccomp-bpf sandbox. Fine to be - // called with threads, as long as - // |options.allow_threads_during_sandbox_init| is true and the kernel - // supports seccomp's TSYNC feature. If TSYNC is not available we treat - // multiple threads as a fatal error. - bool StartSeccompBPF(service_manager::SandboxType sandbox_type, - PreSandboxHook hook, - const Options& options); - - // Limit the address space of the current process (and its children) to make - // some vulnerabilities harder to exploit. Writes the errno due to setrlimit - // (including 0 if no error) into |error|. - bool LimitAddressSpace(int* error); - - // Returns a file descriptor to proc. The file descriptor is no longer valid - // after the sandbox has been sealed. - int proc_fd() const { - DCHECK_NE(-1, proc_fd_); - return proc_fd_; - } - -#if BUILDFLAG(USING_SANITIZER) - __sanitizer_sandbox_arguments* sanitizer_args() const { - return sanitizer_args_.get(); - } -#endif - - // A BrokerProcess is a helper that is started before the sandbox is engaged, - // typically from a pre-sandbox hook, that will serve requests to access - // files over an IPC channel. The client of this runs from a SIGSYS handler - // triggered by the seccomp-bpf sandbox. - // |client_sandbox_policy| is the policy being run by the client, and is - // used to derive the equivalent broker-side policy. - // |broker_side_hook| is an alternate pre-sandbox hook to be run before the - // broker itself gets sandboxed, to which the broker side policy and - // |options| are passed. - // Crashes the process if the broker can not be started since continuation - // is impossible (and presumably unsafe). - // This should never be destroyed, as after the sandbox is started it is - // vital to the process. - void StartBrokerProcess( - const sandbox::syscall_broker::BrokerCommandSet& allowed_command_set, - std::vector<sandbox::syscall_broker::BrokerFilePermission> permissions, - PreSandboxHook broker_side_hook, - const Options& options); - - sandbox::syscall_broker::BrokerProcess* broker_process() const { - return broker_process_; - } - - private: - friend struct base::DefaultSingletonTraits<SandboxLinux>; - - SandboxLinux(); - ~SandboxLinux(); - - // We must have been pre_initialized_ before using these. - bool seccomp_bpf_supported() const; - bool seccomp_bpf_with_tsync_supported() const; - - // Returns true if it can be determined that the current process has open - // directories that are not managed by the SandboxLinux class. This would - // be a vulnerability as it would allow to bypass the setuid sandbox. - bool HasOpenDirectories() const; - - // The last part of the initialization is to make sure any temporary "hole" - // in the sandbox is closed. For now, this consists of closing proc_fd_. - void SealSandbox(); - - // GetStatus() makes promises as to how the sandbox will behave. This - // checks that no promises have been broken. - void CheckForBrokenPromises(service_manager::SandboxType sandbox_type); - - // Stop |thread| and make sure it does not appear in /proc/self/tasks/ - // anymore. - void StopThreadAndEnsureNotCounted(base::Thread* thread) const; - - // Engages the namespace sandbox as described for EngageNamespaceSandbox. - // Returns false if it fails to transition to a new user namespace, but - // after transitioning to a new user namespace we don't allow this function - // to fail. - bool EngageNamespaceSandboxInternal(bool from_zygote); - - // A file descriptor to /proc. It's dangerous to have it around as it could - // allow for sandbox bypasses. It needs to be closed before we consider - // ourselves sandboxed. - int proc_fd_; - - bool seccomp_bpf_started_; - // The value returned by GetStatus(). Gets computed once and then cached. - int sandbox_status_flags_; - // Did PreinitializeSandbox() run? - bool pre_initialized_; - bool seccomp_bpf_supported_; // Accurate if pre_initialized_. - bool seccomp_bpf_with_tsync_supported_; // Accurate if pre_initialized_. - bool yama_is_enforcing_; // Accurate if pre_initialized_. - bool initialize_sandbox_ran_; // InitializeSandbox() was called. - std::unique_ptr<sandbox::SetuidSandboxClient> setuid_sandbox_client_; -#if BUILDFLAG(USING_SANITIZER) - std::unique_ptr<__sanitizer_sandbox_arguments> sanitizer_args_; -#endif - sandbox::syscall_broker::BrokerProcess* broker_process_; // Leaked as global. - - DISALLOW_COPY_AND_ASSIGN(SandboxLinux); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_SANDBOX_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.cc b/chromium/services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.cc deleted file mode 100644 index 1c16d68df91..00000000000 --- a/chromium/services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.cc +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <memory> -#include <utility> - -#include "base/check_op.h" -#include "base/command_line.h" -#include "base/macros.h" -#include "base/notreached.h" -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/bpf_dsl/trap_registry.h" -#include "sandbox/sandbox_buildflags.h" -#include "services/service_manager/sandbox/sandbox_type.h" -#include "services/service_manager/sandbox/switches.h" - -#if BUILDFLAG(USE_SECCOMP_BPF) - -#include "base/files/scoped_file.h" -#include "base/posix/eintr_wrapper.h" -#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h" -#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" -#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" -#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" -#include "sandbox/linux/system_headers/linux_syscalls.h" -#include "services/service_manager/sandbox/linux/bpf_audio_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_cdm_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_cros_amd_gpu_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_cros_arm_gpu_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_gpu_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_network_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_ppapi_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_print_compositor_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_renderer_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_sharing_service_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_speech_recognition_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_utility_policy_linux.h" - -#if !defined(OS_NACL_NONSFI) -#include "services/service_manager/sandbox/chromecast_sandbox_whitelist_buildflags.h" -#endif // !defined(OS_NACL_NONSFI) - -#if defined(OS_CHROMEOS) -#include "services/service_manager/sandbox/linux/bpf_ime_policy_linux.h" -#include "services/service_manager/sandbox/linux/bpf_tts_policy_linux.h" -#endif // defined(OS_CHROMEOS) - -using sandbox::BaselinePolicy; -using sandbox::SandboxBPF; -using sandbox::SyscallSets; -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::ResultExpr; - -#else // BUILDFLAG(USE_SECCOMP_BPF) - -// Make sure that seccomp-bpf does not get disabled by mistake. Also make sure -// that we think twice about this when adding a new architecture. -#if !defined(ARCH_CPU_ARM64) && !defined(ARCH_CPU_MIPS64EL) -#error "Seccomp-bpf disabled on supported architecture!" -#endif // !defined(ARCH_CPU_ARM64) && !defined(ARCH_CPU_MIPS64EL) - -#endif // BUILDFLAG(USE_SECCOMP_BPF) - -namespace service_manager { - -#if BUILDFLAG(USE_SECCOMP_BPF) -namespace { - -#if !defined(OS_NACL_NONSFI) - -// nacl_helper needs to be tiny and includes only part of content/ -// in its dependencies. Make sure to not link things that are not needed. -#if !defined(IN_NACL_HELPER) -inline bool IsChromeOS() { -#if defined(OS_CHROMEOS) - return true; -#else - return false; -#endif -} - -inline bool UseChromecastSandboxWhitelist() { -#if BUILDFLAG(ENABLE_CHROMECAST_GPU_SANDBOX_WHITELIST) - return true; -#else - return false; -#endif -} - -inline bool IsArchitectureArm() { -#if defined(ARCH_CPU_ARM_FAMILY) - return true; -#else - return false; -#endif -} - -std::unique_ptr<BPFBasePolicy> GetGpuProcessSandbox( - bool use_amd_specific_policies) { - if (IsChromeOS() || UseChromecastSandboxWhitelist()) { - if (IsArchitectureArm()) { - return std::make_unique<CrosArmGpuProcessPolicy>( - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kGpuSandboxAllowSysVShm)); - } - if (use_amd_specific_policies) - return std::make_unique<CrosAmdGpuProcessPolicy>(); - } - return std::make_unique<GpuProcessPolicy>(); -} -#endif // !defined(IN_NACL_HELPER) -#endif // !defined(OS_NACL_NONSFI) - -} // namespace - -#endif // USE_SECCOMP_BPF - -// Is seccomp BPF globally enabled? -bool SandboxSeccompBPF::IsSeccompBPFDesired() { -#if BUILDFLAG(USE_SECCOMP_BPF) - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - return !command_line.HasSwitch(switches::kNoSandbox) && - !command_line.HasSwitch(switches::kDisableSeccompFilterSandbox); -#else - return false; -#endif // USE_SECCOMP_BPF -} - -bool SandboxSeccompBPF::SupportsSandbox() { -#if BUILDFLAG(USE_SECCOMP_BPF) - return SandboxBPF::SupportsSeccompSandbox( - SandboxBPF::SeccompLevel::SINGLE_THREADED); -#else - return false; -#endif -} - -#if !defined(OS_NACL_NONSFI) - -bool SandboxSeccompBPF::SupportsSandboxWithTsync() { -#if BUILDFLAG(USE_SECCOMP_BPF) - return SandboxBPF::SupportsSeccompSandbox( - SandboxBPF::SeccompLevel::MULTI_THREADED); -#else - return false; -#endif -} - -std::unique_ptr<BPFBasePolicy> SandboxSeccompBPF::PolicyForSandboxType( - SandboxType sandbox_type, - const SandboxSeccompBPF::Options& options) { - switch (sandbox_type) { - case SandboxType::kGpu: - return GetGpuProcessSandbox(options.use_amd_specific_policies); - case SandboxType::kRenderer: - return std::make_unique<RendererProcessPolicy>(); - case SandboxType::kPpapi: - return std::make_unique<PpapiProcessPolicy>(); - case SandboxType::kUtility: - return std::make_unique<UtilityProcessPolicy>(); - case SandboxType::kCdm: - return std::make_unique<CdmProcessPolicy>(); - case SandboxType::kPrintCompositor: - return std::make_unique<PrintCompositorProcessPolicy>(); - case SandboxType::kNetwork: - return std::make_unique<NetworkProcessPolicy>(); - case SandboxType::kAudio: - return std::make_unique<AudioProcessPolicy>(); - case SandboxType::kSharingService: - return std::make_unique<SharingServiceProcessPolicy>(); - case SandboxType::kSpeechRecognition: - return std::make_unique<SpeechRecognitionProcessPolicy>(); -#if defined(OS_CHROMEOS) - case SandboxType::kIme: - return std::make_unique<ImeProcessPolicy>(); - case SandboxType::kTts: - return std::make_unique<TtsProcessPolicy>(); -#endif // defined(OS_CHROMEOS) - case SandboxType::kZygoteIntermediateSandbox: - case SandboxType::kNoSandbox: - case SandboxType::kVideoCapture: - NOTREACHED(); - return nullptr; - } -} - -// If a BPF policy is engaged for |process_type|, run a few sanity checks. -void SandboxSeccompBPF::RunSandboxSanityChecks( - SandboxType sandbox_type, - const SandboxSeccompBPF::Options& options) { - switch (sandbox_type) { - case SandboxType::kRenderer: - case SandboxType::kGpu: - case SandboxType::kPpapi: - case SandboxType::kPrintCompositor: - case SandboxType::kCdm: { - int syscall_ret; - errno = 0; - - // Without the sandbox, this would EBADF. - syscall_ret = fchmod(-1, 07777); - CHECK_EQ(-1, syscall_ret); - CHECK_EQ(EPERM, errno); - -// Run most of the sanity checks only in DEBUG mode to avoid a perf. -// impact. -#if !defined(NDEBUG) - // open() must be restricted. - syscall_ret = open("/etc/passwd", O_RDONLY); - CHECK_EQ(-1, syscall_ret); - CHECK_EQ(BPFBasePolicy::GetFSDeniedErrno(), errno); - - // We should never allow the creation of netlink sockets. - syscall_ret = socket(AF_NETLINK, SOCK_DGRAM, 0); - CHECK_EQ(-1, syscall_ret); - CHECK_EQ(EPERM, errno); -#endif // !defined(NDEBUG) - } break; -#if defined(OS_CHROMEOS) - case SandboxType::kIme: - case SandboxType::kTts: -#endif // defined(OS_CHROMEOS) - case SandboxType::kAudio: - case SandboxType::kSharingService: - case SandboxType::kSpeechRecognition: - case SandboxType::kNetwork: - case SandboxType::kUtility: - case SandboxType::kNoSandbox: - case SandboxType::kVideoCapture: - case SandboxType::kZygoteIntermediateSandbox: - // Otherwise, no checks required. - break; - } -} -#endif // !defined(OS_NACL_NONSFI) - -bool SandboxSeccompBPF::StartSandboxWithExternalPolicy( - std::unique_ptr<sandbox::bpf_dsl::Policy> policy, - base::ScopedFD proc_fd, - sandbox::SandboxBPF::SeccompLevel seccomp_level) { -#if BUILDFLAG(USE_SECCOMP_BPF) - if (IsSeccompBPFDesired() && SupportsSandbox()) { - CHECK(policy); - // Starting the sandbox is a one-way operation. The kernel doesn't allow - // us to unload a sandbox policy after it has been started. Nonetheless, - // in order to make the use of the "Sandbox" object easier, we allow for - // the object to be destroyed after the sandbox has been started. Note that - // doing so does not stop the sandbox. - SandboxBPF sandbox(std::move(policy)); - sandbox.SetProcFd(std::move(proc_fd)); - CHECK(sandbox.StartSandbox(seccomp_level)); - return true; - } -#endif // BUILDFLAG(USE_SECCOMP_BPF) - return false; -} - -#if !defined(OS_NACL_NONSFI) -std::unique_ptr<sandbox::bpf_dsl::Policy> -SandboxSeccompBPF::GetBaselinePolicy() { -#if BUILDFLAG(USE_SECCOMP_BPF) - return std::make_unique<BaselinePolicy>(); -#else - return nullptr; -#endif // BUILDFLAG(USE_SECCOMP_BPF) -} -#endif // !defined(OS_NACL_NONSFI) - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h b/chromium/services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h deleted file mode 100644 index e9f1688c080..00000000000 --- a/chromium/services/service_manager/sandbox/linux/sandbox_seccomp_bpf_linux.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_SANDBOX_SECCOMP_BPF_LINUX_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_SANDBOX_SECCOMP_BPF_LINUX_H_ - -#include <memory> - -#include "base/callback.h" -#include "base/files/scoped_file.h" -#include "base/macros.h" -#include "build/build_config.h" -#include "sandbox/linux/bpf_dsl/policy.h" -#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/linux/bpf_base_policy_linux.h" -#include "services/service_manager/sandbox/sandbox_type.h" - -namespace service_manager { - -// This class has two main sets of APIs. One can be used to start the sandbox -// for internal content process types, the other is indirectly exposed as -// a public content/ API and uses a supplied policy. -class SERVICE_MANAGER_SANDBOX_EXPORT SandboxSeccompBPF { - public: - struct Options { - bool use_amd_specific_policies = false; // For ChromiumOS. - bool use_intel_specific_policies = false; // For ChromiumOS. - - // Options for GPU's PreSandboxHook. - bool accelerated_video_decode_enabled = false; - bool accelerated_video_encode_enabled = false; - }; - - // This is the API to enable a seccomp-bpf sandbox for content/ - // process-types: - // Is the sandbox globally enabled, can anything use it at all ? - // This looks at global command line flags to see if the sandbox - // should be enabled at all. - static bool IsSeccompBPFDesired(); - - // Check if the kernel supports seccomp-bpf. - static bool SupportsSandbox(); - -#if !defined(OS_NACL_NONSFI) - // Check if the kernel supports TSYNC (thread synchronization) with seccomp. - static bool SupportsSandboxWithTsync(); - - // Return a policy suitable for use with StartSandboxWithExternalPolicy. - static std::unique_ptr<BPFBasePolicy> PolicyForSandboxType( - SandboxType sandbox_type, - const SandboxSeccompBPF::Options& options); - - // Prove that the sandbox was engaged by the StartSandbox() call. Crashes - // the process if the sandbox failed to engage. - static void RunSandboxSanityChecks(SandboxType sandbox_type, - const SandboxSeccompBPF::Options& options); -#endif // !defined(OS_NACL_NONSFI) - - // This is the API to enable a seccomp-bpf sandbox by using an - // external policy. - static bool StartSandboxWithExternalPolicy( - std::unique_ptr<sandbox::bpf_dsl::Policy> policy, - base::ScopedFD proc_fd, - sandbox::SandboxBPF::SeccompLevel seccomp_level = - sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED); - - // The "baseline" policy can be a useful base to build a sandbox policy. - static std::unique_ptr<sandbox::bpf_dsl::Policy> GetBaselinePolicy(); - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(SandboxSeccompBPF); -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_LINUX_SANDBOX_SECCOMP_BPF_LINUX_H_ diff --git a/chromium/services/service_manager/sandbox/mac/BUILD.gn b/chromium/services/service_manager/sandbox/mac/BUILD.gn deleted file mode 100644 index 6bb2025f321..00000000000 --- a/chromium/services/service_manager/sandbox/mac/BUILD.gn +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -action_foreach("package_sb_files") { - script = "package_sb_file.py" - sources = [ - "audio.sb", - "cdm.sb", - "common.sb", - "gpu.sb", - "gpu_v2.sb", - "nacl_loader.sb", - "network.sb", - "ppapi.sb", - "print_compositor.sb", - "renderer.sb", - "utility.sb", - ] - outputs = [ - "$target_gen_dir/{{source_name_part}}.sb.h", - "$target_gen_dir/{{source_name_part}}.sb.cc", - ] - args = [ - "{{source}}", - rebase_path(target_gen_dir, root_build_dir), - ] -} - -source_set("packaged_sb_files") { - sources = get_target_outputs(":package_sb_files") - defines = [ "SERVICE_MANAGER_SANDBOX_IMPL" ] - deps = [ ":package_sb_files" ] -} diff --git a/chromium/services/service_manager/sandbox/mac/DEPS b/chromium/services/service_manager/sandbox/mac/DEPS deleted file mode 100644 index 88962cb8c01..00000000000 --- a/chromium/services/service_manager/sandbox/mac/DEPS +++ /dev/null @@ -1,3 +0,0 @@ -include_rules = [ - "+sandbox/mac", -] diff --git a/chromium/services/service_manager/sandbox/mac/OWNERS b/chromium/services/service_manager/sandbox/mac/OWNERS deleted file mode 100644 index cf797b66465..00000000000 --- a/chromium/services/service_manager/sandbox/mac/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -set noparent -file://sandbox/mac/OWNERS diff --git a/chromium/services/service_manager/sandbox/mac/audio.sb b/chromium/services/service_manager/sandbox/mac/audio.sb deleted file mode 100644 index 2379adcb114..00000000000 --- a/chromium/services/service_manager/sandbox/mac/audio.sb +++ /dev/null @@ -1,67 +0,0 @@ -; Copyright 2018 The Chromium Authors. All rights reserved. -; Use of this source code is governed by a BSD-style license that can be -; found in the LICENSE file. - -; --- The contents of common.sb implicitly included here. --- - -; File access. -(allow file-read* - (path (user-homedir-path "/Library/Caches/com.apple.coreaudio.components.plist")) - (regex (user-homedir-path #"/Library/Preferences/com.apple.coreaudio.*")) - (subpath "/Library/Audio/Plug-Ins") - (subpath "/Library/QuickTime") - (subpath "/System/Library/Components") - (subpath "/System/Library/Extensions") - (subpath (user-homedir-path "/Library/Audio/Plug-Ins")) -) - -(allow device-microphone) - -(allow iokit-open - (iokit-user-client-class "IOAudioControlUserClient") - (iokit-user-client-class "IOAudioEngineUserClient") -) - -(allow ipc-posix-shm-read* ipc-posix-shm-write-data - (ipc-posix-name-regex #"^AudioIO")) - -; Mach IPC. -(allow mach-lookup - (global-name "com.apple.audio.SystemSoundServer-OSX") - (global-name "com.apple.audio.VDCAssistant") - (global-name "com.apple.audio.audiohald") - (global-name "com.apple.audio.coreaudiod") -) - -(if (>= os-version 1013) - (allow mach-lookup - (global-name "com.apple.audio.AudioComponentRegistrar") - (xpc-service-name "com.apple.audio.SandboxHelper") - )) - -; sysctls. -(allow sysctl-read - (sysctl-name "hw.optional.avx1_0") - (sysctl-name "hw.optional.avx2_0") - (sysctl-name "hw.optional.sse2") - (sysctl-name "hw.optional.sse3") - (sysctl-name "hw.optional.sse4_1") - (sysctl-name "hw.optional.sse4_2") -) - -; This is available in 10.15+, and rolled out as a Finch experiment. -(if (param-true? filter-syscalls-debug) - (when (defined? 'syscall-unix) - (deny syscall-unix (with send-signal SIGSYS)) - (allow syscall-unix - (syscall-number SYS_csrctl) - (syscall-number SYS_mlock) - (syscall-number SYS_poll) - (syscall-number SYS_proc_rlimit_control) - (syscall-number SYS_psynch_cvbroad) - (syscall-number SYS_psynch_cvwait) - (syscall-number SYS_setsockopt) - (syscall-number SYS_socketpair) - (syscall-number SYS_work_interval_ctl) - (syscall-number SYS_write) -))) diff --git a/chromium/services/service_manager/sandbox/mac/cdm.sb b/chromium/services/service_manager/sandbox/mac/cdm.sb deleted file mode 100644 index 015d18fc119..00000000000 --- a/chromium/services/service_manager/sandbox/mac/cdm.sb +++ /dev/null @@ -1,16 +0,0 @@ -; Copyright 2017 The Chromium Authors. All rights reserved. -; Use of this source code is governed by a BSD-style license that can be -; found in the LICENSE file. - -; *** The contents of common.sb are implicitly included here. *** - -; Allow preloading of the CDM using seatbelt extension. -(allow file-read* (extension "com.apple.app-sandbox.read")) - -; mach IPC -(allow mach-lookup (global-name "com.apple.windowserver.active")) - -; This is available in 10.15+, and rolled out as a Finch experiment. -(if (param-true? filter-syscalls-debug) - (when (defined? 'syscall-unix) - (deny syscall-unix (with send-signal SIGSYS)))) diff --git a/chromium/services/service_manager/sandbox/mac/common.sb b/chromium/services/service_manager/sandbox/mac/common.sb deleted file mode 100644 index e15e9d97c52..00000000000 --- a/chromium/services/service_manager/sandbox/mac/common.sb +++ /dev/null @@ -1,298 +0,0 @@ -; Copyright 2017 The Chromium Authors. All rights reserved. -; Use of this source code is governed by a BSD-style license that can be -; found in the LICENSE file. -(version 1) - -; Helper function to check if a param is set to true. -(define (param-true? str) (string=? (param str) "TRUE")) - -; Helper function to determine if a parameter is defined or not. -(define (param-defined? str) (string? (param str))) - -; Define constants for all of the parameter strings passed in. -(define browser-pid "BROWSER_PID") -(define bundle-id "BUNDLE_ID") -(define bundle-path "BUNDLE_PATH") -(define component-path "COMPONENT_PATH") -(define current-pid "CURRENT_PID") -(define disable-sandbox-denial-logging "DISABLE_SANDBOX_DENIAL_LOGGING") -(define enable-logging "ENABLE_LOGGING") -(define executable-path "EXECUTABLE_PATH") -(define homedir-as-literal "USER_HOMEDIR_AS_LITERAL") -(define log-file-path "LOG_FILE_PATH") -(define os-version (string->number (param "OS_VERSION"))) -(define darwin-user-cache-dir "DARWIN_USER_CACHE_DIR") -(define darwin-user-dir "DARWIN_USER_DIR") -(define darwin-user-temp-dir "DARWIN_USER_TEMP_DIR") -; There are two separate flags for syscall filtering to allow it -; to be rolled out to one process type at a time, while still allowing -; local development. -(define filter-syscalls "FILTER_SYSCALLS") -(define filter-syscalls-debug "FILTER_SYSCALLS_DEBUG") - -; Backwards compatibility for 10.10. -(if (not (defined? 'path)) - (define path literal)) -; Backwards compatibility for 10.11. -(if (not (defined? 'iokit-registry-entry-class)) - (define iokit-registry-entry-class iokit-user-client-class)) - -; --enable-sandbox-logging causes the sandbox to log failures to the syslog. -(if (param-true? disable-sandbox-denial-logging) - (deny default (with no-log)) - (deny default)) - -(if (param-true? enable-logging) (debug deny)) - -; Allow sending signals to self - https://crbug.com/20370 -(allow signal (target self)) - -; Consumes a subpath and appends it to the user's homedir path. -(define (user-homedir-path subpath) - (string-append (param homedir-as-literal) subpath)) - -; A function that specific profiles (i.e. renderer) can call to allow -; font rendering. -(define (allow-font-access) - (begin - (allow file-read-data - (subpath "/Library/Fonts") - (subpath "/System/Library/Fonts") - (subpath (user-homedir-path "/Library/Fonts")) - ) - (allow mach-lookup - ; https://crbug.com/756145, https://crbug.com/786615 - (global-name "com.apple.FontObjectsServer") - (global-name "com.apple.fonts") - ) - (if (< os-version 1012) - (allow mach-lookup (global-name "com.apple.FontServer"))) - ; To allow accessing downloaded and other hidden fonts in - ; /System/Library/Asssets/com_apple_MobileAsset_Font*. - ; (https://crbug.com/662686) - (allow file-read* (extension "com.apple.app-sandbox.read")))) - -; Reads of signed Mach-O blobs created by the CVMS server. -; https://crbug.com/850021 -(define (allow-cvms-blobs) - (if (>= os-version 1014) - (begin - (allow file-read* file-write-unlink - (prefix "/private/tmp/cvmsCodeSignObj")) - (allow file-read* - (extension "com.apple.cvms.kernel") - (prefix "/private/var/db/CVMS/cvmsCodeSignObj")) -))) - -; Allow logging for all processes. -(allow file-write* - (require-all - (path (param log-file-path)) - (vnode-type REGULAR-FILE))) - -; Allow component builds to work. -(if (param-defined? component-path) - (allow file-read* (subpath (param component-path)))) - -(allow process-exec (path (param executable-path))) -(allow file-read* (path (param executable-path))) - -; The browser exposes Mach services at "bundle-id.service-name.browser-pid". -; This macro is a helper for doing the string concatenation. -(define (browser-service-name service-name) - (global-name (string-append (param bundle-id) - "." service-name "." - (param browser-pid)))) - -(allow mach-lookup - (browser-service-name "MachPortRendezvousServer") -) - -; Allow realpath() to work. -(allow file-read-metadata (subpath "/")) - -; All processes can read the bundle contents. -(allow file-read* (subpath (param bundle-path))) - -; Allow reads of system libraries and frameworks. -(allow file-read* - (subpath "/System/Library/CoreServices/CoreTypes.bundle") - (subpath "/System/Library/CoreServices/SystemVersion.bundle") - (subpath "/System/Library/Frameworks") - (subpath "/System/Library/Preferences/Logging") - (subpath "/System/Library/PrivateFrameworks") - (subpath "/usr/lib") -) - -; Reads from /etc. -; This is read by CFPrefs calling getpwuid in a loop. libinfo then fails to -; contact any of the opendirectoryd mach services, and falls back to -; the /etc/passwd file for the user info. The access is OK because -; no actual password hashes are in /etc/passwd anymore. -(allow file-read-data (path "/private/etc/passwd")) - -; Access to /dev. -(allow file-ioctl file-read-data file-write-data - (require-all - (path "/dev/dtracehelper") - (vnode-type CHARACTER-DEVICE))) - -(allow file-read-data - (path "/dev/null") - (path "/dev/random") - (path "/dev/urandom") -) - -(if (>= os-version 1013) - (begin (allow file-read* (subpath "/private/var/db/timezone")) - (allow file-read-data (subpath "/usr/share/zoneinfo.default")))) - -(if (< os-version 1013) - (allow file-read-data (subpath "/usr/share/zoneinfo"))) - -; Reads from /Library. -(allow file-read-data - (path "/Library/Preferences/.GlobalPreferences.plist") -) - -; Reads from /System. -(allow file-read-data - (path "/System/Library/CoreServices/SystemVersion.plist") - (path "/System/Library/CoreServices/checkfixlist") -) - -; Reads from /usr. -(allow file-read-data - (subpath "/usr/share/icu") -) - -; Access to the home directory. -(allow file-read-data - (path (string-append (user-homedir-path "/Library/Preferences/") (param bundle-id) ".plist")) - (path (user-homedir-path "/Library/Preferences/.GlobalPreferences.plist")) - (regex (user-homedir-path #"/Library/Preferences/ByHost/.GlobalPreferences.*")) - (path (user-homedir-path "/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist")) -) - -; Mach IPC needed by all Chromium Helper instances. -(allow mach-lookup - (global-name "com.apple.logd") ; https://crbug.com/792229 - (global-name "com.apple.system.logger") - (global-name "com.apple.system.opendirectoryd.libinfo") ; https://crbug.com/792228 -) - -; sysctls permitted. -(allow sysctl-read - (sysctl-name "hw.activecpu") - (sysctl-name "hw.busfrequency_compat") - (sysctl-name "hw.byteorder") - (sysctl-name "hw.cachelinesize_compat") - (sysctl-name "hw.cpufrequency_compat") - (sysctl-name "hw.cputype") - (sysctl-name "hw.logicalcpu_max") - (sysctl-name "hw.machine") - (sysctl-name "hw.ncpu") - (sysctl-name "hw.pagesize_compat") - (sysctl-name "hw.physicalcpu_max") - (sysctl-name "hw.tbfrequency_compat") - (sysctl-name "hw.vectorunit") - (sysctl-name "kern.hostname") - (sysctl-name "kern.maxfilesperproc") - (sysctl-name "kern.osrelease") - (sysctl-name "kern.ostype") - (sysctl-name "kern.osvariant_status") - (sysctl-name "kern.osversion") - (sysctl-name "kern.usrstack64") - (sysctl-name "kern.version") - (sysctl-name "sysctl.proc_cputype") - (sysctl-name (string-append "kern.proc.pid." (param current-pid))) -) - -(allow network-outbound - (literal "/private/var/run/asl_input") - (literal "/private/var/run/syslog") -) - -; This is available in 10.15+, and rolled out as a Finch experiment. -(if (param-true? filter-syscalls) - (when (defined? 'syscall-unix) - (allow syscall-unix - (syscall-number SYS___disable_threadsignal) - (syscall-number SYS___mac_syscall) - (syscall-number SYS___pthread_kill) - (syscall-number SYS___pthread_markcancel) - (syscall-number SYS___pthread_sigmask) - (syscall-number SYS___semwait_signal) - (syscall-number SYS___semwait_signal_nocancel) - (syscall-number SYS_access) - (syscall-number SYS_bsdthread_create) - (syscall-number SYS_bsdthread_ctl) - (syscall-number SYS_bsdthread_register) - (syscall-number SYS_bsdthread_terminate) - (syscall-number SYS_close) - (syscall-number SYS_close_nocancel) - (syscall-number SYS_csops_audittoken) - (syscall-number SYS_exit) - (syscall-number SYS_fcntl) - (syscall-number SYS_fileport_makefd) - (syscall-number SYS_fileport_makeport) - (syscall-number SYS_fstat64) - (syscall-number SYS_fstatat64) - (syscall-number SYS_fstatfs64) - (syscall-number SYS_getattrlist) - (syscall-number SYS_getattrlistbulk) - (syscall-number SYS_getaudit_addr) - (syscall-number SYS_getdirentries64) - (syscall-number SYS_geteuid) - (syscall-number SYS_getgid) - (syscall-number SYS_gethostuuid) - (syscall-number SYS_getpid) - (syscall-number SYS_getppid) - (syscall-number SYS_getpriority) - (syscall-number SYS_getrlimit) - (syscall-number SYS_gettimeofday) - (syscall-number SYS_getuid) - (syscall-number SYS_ioctl) - (syscall-number SYS_issetugid) - (syscall-number SYS_kdebug_trace64) - (syscall-number SYS_kevent64) - (syscall-number SYS_kevent_id) - (syscall-number SYS_kevent_qos) - (syscall-number SYS_kqueue) - (syscall-number SYS_lstat64) - (syscall-number SYS_madvise) - (syscall-number SYS_mmap) - (syscall-number SYS_mprotect) - (syscall-number SYS_munmap) - (syscall-number SYS_open) - (syscall-number SYS_open_dprotected_np) - (syscall-number SYS_open_nocancel) - (syscall-number SYS_pread) - (syscall-number SYS_proc_info) - (syscall-number SYS_psynch_mutexdrop) - (syscall-number SYS_psynch_mutexwait) - (syscall-number SYS_psynch_rw_downgrade) - (syscall-number SYS_psynch_rw_longrdlock) - (syscall-number SYS_psynch_rw_rdlock) - (syscall-number SYS_psynch_rw_unlock) - (syscall-number SYS_psynch_rw_unlock2) - (syscall-number SYS_psynch_rw_upgrade) - (syscall-number SYS_psynch_rw_wrlock) - (syscall-number SYS_psynch_rw_yieldwrlock) - (syscall-number SYS_read) - (syscall-number SYS_read_nocancel) - (syscall-number SYS_readlink) - (syscall-number SYS_shm_open) - (syscall-number SYS_sigaction) - (syscall-number SYS_sigprocmask) - (syscall-number SYS_sigreturn) - (syscall-number SYS_stat64) - (syscall-number SYS_statfs64) - (syscall-number SYS_sysctl) - (syscall-number SYS_sysctlbyname) - (syscall-number SYS_thread_selfid) - (syscall-number SYS_ulock_wait) - (syscall-number SYS_ulock_wake) - (syscall-number SYS_workq_kernreturn) - (syscall-number SYS_workq_open) -))) diff --git a/chromium/services/service_manager/sandbox/mac/gpu.sb b/chromium/services/service_manager/sandbox/mac/gpu.sb deleted file mode 100644 index c02bbd5c18f..00000000000 --- a/chromium/services/service_manager/sandbox/mac/gpu.sb +++ /dev/null @@ -1,99 +0,0 @@ -;; -;; Copyright (c) 2011 The Chromium Authors. All rights reserved. -;; Use of this source code is governed by a BSD-style license that can be -;; found in the LICENSE file. -;; - -; *** The contents of the V1 sandbox common.sb are below. *** - -(version 1) - -; Helper function to check if a param is set to true. -(define (param-true? str) (string=? (param str) "TRUE")) - -; Helper function to determine if a parameter is defined or not. -(define (param-defined? str) (string? (param str))) - -; Define constants for all of the parameter strings passed in. -(define bundle-version-path "BUNDLE_VERSION_PATH") -(define disable-sandbox-denial-logging "DISABLE_SANDBOX_DENIAL_LOGGING") -(define enable-logging "ENABLE_LOGGING") -(define homedir-as-literal "USER_HOMEDIR_AS_LITERAL") -(define elcap-or-later "ELCAP_OR_LATER") -(define macos-1013 "MACOS_1013") -(define field-trial-server-name "FIELD_TRIAL_SERVER_NAME") - -; Backwards compatibility for 10.9 -(if (not (defined? 'path)) - (define path literal)) -(if (not (defined? 'iokit-registry-entry-class)) - (define iokit-registry-entry-class iokit-user-client-class)) - -; Consumes a subpath and appends it to the user's homedir path. -(define (user-homedir-path subpath) - (string-append (param homedir-as-literal) subpath)) - -; (path) is not supported until 10.10. -; TODO(kerrnel): remove this when 10.9 is no longer supported. -(define (path x) (literal x)) - -; DISABLE_SANDBOX_DENIAL_LOGGING turns off log messages in the system log. -(if (param-true? disable-sandbox-denial-logging) - (deny default (with no-log)) - (deny default)) - -; Support for programmatically enabling verbose debugging. -(if (param-true? enable-logging) (debug deny)) - -(allow mach-lookup (global-name (param field-trial-server-name))) - -; Allow sending signals to self - https://crbug.com/20370 -(allow signal (target self)) - -; Needed for full-page-zoomed controls - https://crbug.com/11325 -(allow sysctl-read) - -; Loading System Libraries. -(allow file-read* - (subpath "/System/Library/Frameworks") - (subpath "/System/Library/PrivateFrameworks") - (subpath "/System/Library/CoreServices")) - -(allow ipc-posix-shm) - -; Allow direct access to /dev/urandom, similar to Linux/POSIX, to allow -; third party code (eg: bits of Adobe Flash and NSS) to function properly. -(allow file-read-data file-read-metadata (literal "/dev/urandom")) - -; *** The contents of the V1 sandbox gpu.sb are below. *** - -; Allow communication between the GPU process and the UI server. -(allow mach-lookup (global-name "com.apple.tsm.uiserver")) - -(allow file-read-metadata (literal "/")) - -; Needed for WebGL - crbug.com/75343 -(allow iokit-open - (iokit-connection "IOAccelerator") - (iokit-user-client-class "IOAccelerationUserClient") - (iokit-user-client-class "IOFramebufferSharedUserClient") - (iokit-user-client-class "AppleGraphicsControlClient") - (iokit-user-client-class "AGPMClient") - (iokit-user-client-class "IOHIDParamUserClient") - (iokit-user-client-class "RootDomainUserClient") - (iokit-user-client-class "IOSurfaceRootUserClient") - (iokit-user-client-class "IOSurfaceSendRight")) - -; https://crbug.com/515280 -(if (param-true? elcap-or-later) - (allow file-read* (subpath "/System/Library/Extensions"))) - -; Needed for VideoToolbox usage - https://crbug.com/767037 -(allow mach-lookup (global-name "com.apple.coremedia.videodecoder")) - -; Needed for 10.14.5+ - https://crbug.com/957217 -(if (defined? 'xpc-service-name) - (allow mach-lookup (xpc-service-name "com.apple.MTLCompilerService"))) - -; Needed for GPU process to fallback to SwiftShader - https://crbug.com/897914 -(allow file-read-data file-read-metadata (subpath (param bundle-version-path))) diff --git a/chromium/services/service_manager/sandbox/mac/gpu_v2.sb b/chromium/services/service_manager/sandbox/mac/gpu_v2.sb deleted file mode 100644 index 66f1bf89cca..00000000000 --- a/chromium/services/service_manager/sandbox/mac/gpu_v2.sb +++ /dev/null @@ -1,116 +0,0 @@ -; Copyright 2017 The Chromium Authors. All rights reserved. -; Use of this source code is governed by a BSD-style license that can be -; found in the LICENSE file. - -; --- The contents of common.sb implicitly included here. --- - -; Allow cf prefs to work. -(allow user-preference-read) - -(allow-cvms-blobs) - -(allow ipc-posix-shm) - -; Allow communication between the GPU process and the UI server. -(allow mach-lookup - (global-name "com.apple.bsd.dirhelper") - (global-name "com.apple.cfprefsd.agent") - (global-name "com.apple.cfprefsd.daemon") - (global-name "com.apple.CoreServices.coreservicesd") - (global-name "com.apple.coreservices.launchservicesd") - (global-name "com.apple.cvmsServ") - (global-name "com.apple.gpumemd.source") - (global-name "com.apple.lsd.mapdb") - (global-name "com.apple.lsd.modifydb") - (global-name "com.apple.powerlog.plxpclogger.xpc") - (global-name "com.apple.PowerManagement.control") - (global-name "com.apple.SecurityServer") - (global-name "com.apple.system.notification_center") - (global-name "com.apple.tsm.uiserver") - (global-name "com.apple.windowserver.active") -) - -; Needed for metal decoding - https://crbug.com/957217 -(if (>= os-version 1014) - (allow mach-lookup (xpc-service-name "com.apple.MTLCompilerService")) -) - -; Needed for WebGL - https://crbug.com/75343 -(allow iokit-open - (iokit-connection "IOAccelerator") - (iokit-user-client-class "AGPMClient") - (iokit-user-client-class "AppleGraphicsControlClient") - (iokit-user-client-class "AppleGraphicsPolicyClient") - (iokit-user-client-class "AppleIntelMEUserClient") - (iokit-user-client-class "AppleMGPUPowerControlClient") - (iokit-user-client-class "AppleSNBFBUserClient") - (iokit-user-client-class "IOAccelerationUserClient") - (iokit-user-client-class "IOFramebufferSharedUserClient") - (iokit-user-client-class "IOHIDParamUserClient") - (iokit-user-client-class "IOSurfaceRootUserClient") - (iokit-user-client-class "IOSurfaceSendRight") - (iokit-user-client-class "RootDomainUserClient") -) - -(allow iokit-set-properties - (require-all (iokit-connection "IODisplay") - (require-any (iokit-property "brightness") - (iokit-property "linear-brightness") - (iokit-property "commit") - (iokit-property "rgcs") - (iokit-property "ggcs") - (iokit-property "bgcs") -))) - -(allow ipc-posix-shm-read-data - (ipc-posix-name "apple.shm.notification_center")) - -; https://crbug.com/515280 -(if (>= os-version 1011) - (allow file-read* (subpath "/System/Library/Extensions"))) - -; Needed for VideoToolbox usage - https://crbug.com/767037 -(if (>= os-version 1013) - (allow mach-lookup - (xpc-service-name "com.apple.coremedia.videodecoder") - (xpc-service-name "com.apple.coremedia.videoencoder") - (xpc-service-name-regex #"\.apple-extension-service$") -)) - -(allow sysctl-read - (sysctl-name "hw.logicalcpu_max") - (sysctl-name "hw.model") - (sysctl-name "kern.osvariant_status") -) - -(allow file-read-data - (path "/Library/MessageTracer/SubmitDiagInfo.default.domains.searchtree") - (regex (user-homedir-path #"/Library/Preferences/ByHost/com.apple.AppleGVA.*")) -) - -(allow file-read* - (subpath "/Library/GPUBundles") - (subpath "/Library/Video/Plug-Ins") - (subpath "/System/Library/CoreServices/RawCamera.bundle") - (subpath "/System/Library/Video/Plug-Ins") -) - -; crbug.com/980134 -(allow file-read* file-write* - (subpath (param darwin-user-cache-dir)) - (subpath (param darwin-user-dir)) - (subpath (param darwin-user-temp-dir)) -) - -(if (param-true? filter-syscalls-debug) - (when (defined? 'syscall-unix) - (deny syscall-unix (with send-signal SIGSYS)) - (allow syscall-unix - (syscall-number SYS_csrctl) - (syscall-number SYS_getentropy) - (syscall-number SYS_getxattr) - (syscall-number SYS_kdebug_typefilter) - (syscall-number SYS_sigaltstack) - (syscall-number SYS_write) - (syscall-number SYS_write_nocancel) -))) diff --git a/chromium/services/service_manager/sandbox/mac/nacl_loader.sb b/chromium/services/service_manager/sandbox/mac/nacl_loader.sb deleted file mode 100644 index a5014f76413..00000000000 --- a/chromium/services/service_manager/sandbox/mac/nacl_loader.sb +++ /dev/null @@ -1,45 +0,0 @@ -;; -;; Copyright (c) 2011 The Chromium Authors. All rights reserved. -;; Use of this source code is governed by a BSD-style license that can be -;; found in the LICENSE file. -;; -; This is the Sandbox configuration file used for safeguarding the user's -; untrusted code within Native Client. -; - -; *** The contents of common.sb are implicitly included here. *** - -; Allow a Native Client application to use semaphores, specifically -; sem_init(), et.al. -(allow ipc-posix-sem) - -(allow user-preference-read) - -(allow iokit-get-properties - (iokit-registry-entry-class "IORegisterForSystemPower")) - -(allow iokit-open - (iokit-user-client-class "IOSurfaceSendRight") - (iokit-user-client-class "RootDomainUserClient") -) - -(allow file-read-data - (subpath "/usr/share/locale") - ; e.g. ~/Library/Preferences/com.google.Chrome.plist - (path (string-append - (user-homedir-path "/Library/Preferences/") - (param bundle-id) - ".plist")) -) - -(allow mach-lookup - (global-name "com.apple.PowerManagement.control") - (global-name "com.apple.system.notification_center") -) - -(if (>= os-version 1014) - (begin - (allow sysctl-read (sysctl-name "kern.tcsm_enable")) - (allow sysctl-write (sysctl-name "kern.tcsm_enable")) - (allow sysctl-read (sysctl-name "kern.tcsm_available")) -)) diff --git a/chromium/services/service_manager/sandbox/mac/network.sb b/chromium/services/service_manager/sandbox/mac/network.sb deleted file mode 100644 index 9107f3704ba..00000000000 --- a/chromium/services/service_manager/sandbox/mac/network.sb +++ /dev/null @@ -1,124 +0,0 @@ -; Copyright 2018 The Chromium Authors. All rights reserved. -; Use of this source code is governed by a BSD-style license that can be -; found in the LICENSE file. - -; --- The contents of common.sb implicitly included here. --- - -; Injected parameters. -(define network-service-storage-paths-count "NETWORK_SERVICE_STORAGE_PATHS_COUNT") -(define network-service-storage-path-n "NETWORK_SERVICE_STORAGE_PATH_") -(define network-service-test-certs-dir "NETWORK_SERVICE_TEST_CERTS_DIR") - -; Allow access to the [0,N) storage location paths. -(let ((count (string->number (param network-service-storage-paths-count)))) - (let loop ((i 0)) - (if (< i count) - (begin - (allow file* (subpath - (param (string-append network-service-storage-path-n (number->string i))))) - (loop (+ i 1)))))) - -; DNS configuration watcher entries. This is a nesty mess of symlinks. -(allow file-read* - (path "/") - (path "/etc") - (path "/etc/hosts") - (path "/etc/resolv.conf") - (path "/private") - (path "/private/etc") - (path "/private/etc/hosts") - (path "/private/etc/resolv.conf") - (path "/private/var") - (path "/private/var/run") - (path "/private/var/run/resolv.conf") - (path "/var") - (path "/var/run") -) - -; Local preferences. -(allow file-read* - (path (user-homedir-path (string-append "/Library/Preferences/" (param bundle-id) ".plist"))) -) - -; Certificate databases. -(allow file-read* - (path "/Library/Preferences/com.apple.security.plist") - ; https://crbug.com/1024000 - (path (user-homedir-path "/Library/Preferences/com.apple.security.revocation.plist")) - (subpath "/Library/Keychains") - (subpath "/System/Library/Keychains") - (subpath "/System/Library/Security") - (subpath "/private/var/db/mds") - (subpath (user-homedir-path "/Library/Keychains")) -) -(allow file-read* file-write* - (subpath (param darwin-user-cache-dir)) - (subpath (param darwin-user-temp-dir)) -) -(if (param-defined? network-service-test-certs-dir) - (allow file-read* (subpath (param network-service-test-certs-dir)))) - -; Network socket access. -(allow network-outbound - (control-name "com.apple.netsrc") - (literal "/private/var/run/mDNSResponder") - (remote tcp) - (remote udp) -) -(allow network-bind network-inbound - (local tcp) - (local udp) -) - -; DNS resolution. -(allow system-socket - (require-all (socket-domain AF_SYSTEM) - (socket-protocol 2)) ; SYSPROTO_CONTROL - (socket-domain AF_ROUTE) -) - -; Distributed notifications memory. -(allow ipc-posix-shm-read-data - (ipc-posix-name "apple.shm.notification_center") -) - -; Notification data from the security server database. -(allow ipc-posix-shm - (ipc-posix-name "com.apple.AppleDatabaseChanged") -) - -(allow mach-lookup - ; Set backup exclusion on cache files. - (global-name "com.apple.backupd.sandbox.xpc") - - ; Used to look up the _CS_DARWIN_USER_CACHE_DIR in the sandbox. - (global-name "com.apple.bsd.dirhelper") - (global-name "com.apple.system.opendirectoryd.membership") - - ; Allow notifications of DNS changes. - (global-name "com.apple.system.notification_center") - - ; Communicate with the security server for TLS certificate information. - (global-name "com.apple.SecurityServer") - (global-name "com.apple.networkd") ; https://crbug.com/1024000 - (global-name "com.apple.ocspd") - (global-name "com.apple.trustd.agent") - - ; Read network configuration. - (global-name "com.apple.SystemConfiguration.DNSConfiguration") - (global-name "com.apple.SystemConfiguration.configd") -) - -(allow sysctl-read - (sysctl-name-regex #"^net.routetable") -) - -; Kerberos support. This should be removed after GSS is moved out of the -; network service. https://crbug.com/1017830 -(allow mach-lookup - (global-name "org.h5l.kcm") -) -(allow file-read* - (path "/private/etc/krb5.conf") - (subpath "/System/Library/KerberosPlugins/KerberosFrameworkPlugins") -) diff --git a/chromium/services/service_manager/sandbox/mac/package_sb_file.py b/chromium/services/service_manager/sandbox/mac/package_sb_file.py deleted file mode 100755 index 098d689a6c6..00000000000 --- a/chromium/services/service_manager/sandbox/mac/package_sb_file.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import os -import sys - -"""Pack MacOS sandbox seatbelt .sb files as C-style strings, escaping -quotes and backslashes as needed. -""" - -header = '// Generated by package_sb_file.py. Do not edit !!!\n\n' -namespace = 'namespace service_manager {\n\n' -namespace_end = '\n} // namespace service_manager\n' -h_include = '#include "services/service_manager/sandbox/export.h"\n' -h_definition = ('SERVICE_MANAGER_SANDBOX_EXPORT\n' + - 'extern const char kSeatbeltPolicyString_%s[];\n\n') -cc_include = '#include "services/service_manager/sandbox/mac/%s.sb.h"\n' -cc_definition = 'const char kSeatbeltPolicyString_%s[] = \n' -cc_definition_end = '"";\n' # Add "" so the definition has some content - # (the empty string) if the sb file is empty. - -def escape_for_c(line): - if line and line[0] == ';': - return '' - return line.replace('\\', '\\\\').replace('\"', '\\\"') - -def pack_file(argv): - if len(argv) != 2: - print >> sys.stderr, 'usage: package_sb_file.py input_filename output_dir' - return 1 - input_filename = argv[0] - output_directory = argv[1] - input_basename = os.path.basename(input_filename) - (module_name, module_ext) = os.path.splitext(input_basename) - output_h_file = output_directory + '/' + input_basename + '.h' - output_cc_file = output_directory + '/' + input_basename + '.cc' - try: - with open(input_filename, 'rb') as infile: - with open(output_h_file, 'wb') as outfile: - outfile.write(header) - outfile.write(h_include) - outfile.write(namespace) - outfile.write(h_definition % module_name) - outfile.write(namespace_end) - with open(output_cc_file, 'wb') as outfile: - outfile.write(header) - outfile.write(cc_include % module_name) - outfile.write(namespace) - outfile.write(cc_definition % module_name) - for line in infile: - escaped_line = escape_for_c(line.rstrip()) - if escaped_line: - outfile.write(' "' + escaped_line + '\\n"\n') - outfile.write(cc_definition_end) - outfile.write(namespace_end) - except IOError: - print >> sys.stderr, 'Failed to process %s' % input_filename - return 1 - return 0 - -if __name__ == '__main__': - sys.exit(pack_file(sys.argv[1:])) diff --git a/chromium/services/service_manager/sandbox/mac/ppapi.sb b/chromium/services/service_manager/sandbox/mac/ppapi.sb deleted file mode 100644 index 6c0ca29e1f3..00000000000 --- a/chromium/services/service_manager/sandbox/mac/ppapi.sb +++ /dev/null @@ -1,49 +0,0 @@ -; Copyright 2017 The Chromium Authors. All rights reserved. -; Use of this source code is governed by a BSD-style license that can be -; found in the LICENSE file. - -; --- The contents of common.sb implicitly included here. --- - -; Params specific to ppapi. -(define ppapi-plugin-0 "PPAPI_PATH_0") -(define ppapi-plugin-1 "PPAPI_PATH_1") -(define ppapi-plugin-2 "PPAPI_PATH_2") -(define ppapi-plugin-3 "PPAPI_PATH_3") -(define ppapi-plugin-4 "PPAPI_PATH_4") - -; Needed for Fonts. -(allow-font-access) - -; Mach lookups. -(allow mach-lookup - (global-name "com.apple.windowserver.active") -) - -; IOKit -(allow iokit-open - (iokit-registry-entry-class "IOSurfaceRootUserClient") -) - -; Reads from home dir. -(allow file-read-data - (path (user-homedir-path "/Library/Preferences/com.apple.universalaccess.plist")) -) - -; Reads from /System. -(allow file-read-data - (path "/System/Library/Colors/System.clr/System.clr") - (subpath "/System/Library/ColorSync/Profiles") ; https://crbug.com/822218 - (subpath "/System/Library/CoreServices/SystemAppearance.bundle") -) - -; Allow the ppapi plugin binaries to be loaded. -(if (param-defined? ppapi-plugin-0) - (allow file-read-data (subpath (param ppapi-plugin-0)))) -(if (param-defined? ppapi-plugin-1) - (allow file-read-data (subpath (param ppapi-plugin-1)))) -(if (param-defined? ppapi-plugin-2) - (allow file-read-data (subpath (param ppapi-plugin-2)))) -(if (param-defined? ppapi-plugin-3) - (allow file-read-data (subpath (param ppapi-plugin-3)))) -(if (param-defined? ppapi-plugin-4) - (allow file-read-data (subpath (param ppapi-plugin-4)))) diff --git a/chromium/services/service_manager/sandbox/mac/print_compositor.sb b/chromium/services/service_manager/sandbox/mac/print_compositor.sb deleted file mode 100644 index a8287999a5a..00000000000 --- a/chromium/services/service_manager/sandbox/mac/print_compositor.sb +++ /dev/null @@ -1,33 +0,0 @@ -;; Copyright 2018 The Chromium Authors. All rights reserved. -;; Use of this source code is governed by a BSD-style license that can be -;; found in the LICENSE file. -;; -; This is the sandbox configuration file used for safeguarding the print -; compositor service which is used for compositing web contents printed from -; different renderer processes. -; -; This configuration locks everything down, except font accesses. -; - -; *** The contents of common.sb are implicitly included here. *** - -; Needed for Fonts. -(allow-font-access) - -; Reads from /System. -(allow file-read-data - (subpath "/System/Library/ColorSync/Profiles") ; https://crbug.com/822218 -) - -; This is available in 10.15+, and rolled out as a Finch experiment. -(if (param-true? filter-syscalls-debug) - (when (defined? 'syscall-unix) - (deny syscall-unix (with send-signal SIGSYS)) - (allow syscall-unix - (syscall-number SYS_fsgetpath) - (syscall-number SYS_getfsstat64) - (syscall-number SYS_mkdir) - (syscall-number SYS_pathconf) - (syscall-number SYS_sigaltstack) - (syscall-number SYS_write) -))) diff --git a/chromium/services/service_manager/sandbox/mac/renderer.sb b/chromium/services/service_manager/sandbox/mac/renderer.sb deleted file mode 100644 index 5071b8ce7ee..00000000000 --- a/chromium/services/service_manager/sandbox/mac/renderer.sb +++ /dev/null @@ -1,193 +0,0 @@ -; Copyright 2017 The Chromium Authors. All rights reserved. -; Use of this source code is governed by a BSD-style license that can be -; found in the LICENSE file. - -; --- The contents of common.sb implicitly included here. --- - -; Put the denials first. -; crbug.com/799149: These operations are allowed by default. -(if (>= os-version 1013) - (if (param-true? disable-sandbox-denial-logging) - (deny iokit-get-properties process-info* nvram* (with no-log)) - (deny iokit-get-properties process-info* nvram*) -)) - -; Allow cf prefs to work. -(allow user-preference-read) - -; process-info -(if (>= os-version 1013) - (begin - (allow process-info-pidinfo) - (allow process-info-setcontrol (target self)) -)) - -; File reads. -; Reads from the home directory. -(allow file-read-data - (path (user-homedir-path "/.CFUserTextEncoding")) - (path (user-homedir-path "/Library/Preferences/com.apple.universalaccess.plist")) -) - -; Reads of /dev devices. -(allow file-read-data - (path "/dev/autofs_nowait") - (path "/dev/fd") -) - -(allow-cvms-blobs) - -(allow file-write-data - (require-all - (path "/dev/null") - (vnode-type CHARACTER-DEVICE))) - -; Needed for Fonts. -(allow-font-access) - -; Reads from /System. -(allow file-read-data - (path "/System/Library/CoreServices/CoreTypes.bundle/Contents/Library/AppExceptions.bundle/Exceptions.plist") - (path "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/Exceptions.plist") - (path "/System/Library/Preferences/Logging/Subsystems/com.apple.SkyLight.plist") - (subpath "/System/Library/ColorSync/Profiles") - (subpath "/System/Library/CoreServices/SystemAppearance.bundle") - (subpath "/System/Library/CoreServices/SystemVersion.bundle") - (subpath "/System/Library/Extensions") ; https://crbug.com/847518 - (subpath "/System/Library/LinguisticData") -) - -; Reads from /Library. -(allow file-read-data - (subpath "/Library/GPUBundles") ; https://crbug.com/850021 -) - -; IOKit -(allow iokit-open - (iokit-registry-entry-class "IOSurfaceRootUserClient") - (iokit-registry-entry-class "RootDomainUserClient") - (iokit-user-client-class "IOSurfaceSendRight") -) - -; POSIX IPC -(allow ipc-posix-shm-read-data - (ipc-posix-name "apple.cfprefs.317580v1") - (ipc-posix-name "apple.cfprefs.daemonv1") - (ipc-posix-name "apple.shm.notification_center") ; https://crbug.com/792217 -) - -; mach IPC -(allow mach-lookup - (global-name "com.apple.cvmsServ") ; https://crbug.com/850021 - (global-name "com.apple.distributed_notifications@Uv3") ; https://crbug.com/792257 - (global-name "com.apple.lsd.mapdb") - (global-name "com.apple.system.notification_center") ; https://crbug.com/792217 -) - -; IOKit properties. -(if (>= os-version 1013) - (allow iokit-get-properties - (iokit-property "CaseSensitive") - (iokit-property "CoreStorage Encrypted") - (iokit-property "Ejectable") - (iokit-property "Encrypted") - (iokit-property "IOClassNameOverride") - (iokit-property "IOMediaIcon") - (iokit-property "Product Identification") - (iokit-property "Protocol Characteristics") - (iokit-property "Removable") - (iokit-property "image-encrypted") -)) - -; For V8 to use in thread calculations. -(if (>= os-version 1014) - (begin - (allow sysctl-read (sysctl-name "kern.tcsm_enable")) - (allow sysctl-write (sysctl-name "kern.tcsm_enable")) - (allow sysctl-read (sysctl-name "kern.tcsm_available")) -)) - -; This is available in 10.15+, and rolled out as a Finch experiment. -(if (param-true? filter-syscalls) - (when (defined? 'syscall-unix) - (deny syscall-unix (with send-signal SIGSYS)) - (allow syscall-unix - (syscall-number SYS_change_fdguard_np) - (syscall-number SYS_chdir) - (syscall-number SYS_chmod) - (syscall-number SYS_csops) - (syscall-number SYS_csrctl) - (syscall-number SYS_dup) - (syscall-number SYS_dup2) - (syscall-number SYS_fchmod) - (syscall-number SYS_fcntl_nocancel) - (syscall-number SYS_fgetxattr) - (syscall-number SYS_fileport_makefd) - (syscall-number SYS_fileport_makeport) - (syscall-number SYS_flock) - (syscall-number SYS_fsetattrlist) - (syscall-number SYS_fsgetpath) - (syscall-number SYS_fsync) - (syscall-number SYS_ftruncate) - (syscall-number SYS_getegid) - (syscall-number SYS_getentropy) - (syscall-number SYS_getfsstat64) - (syscall-number SYS_getrusage) - (syscall-number SYS_getsockopt) - (syscall-number SYS_gettid) - (syscall-number SYS_getxattr) - (syscall-number SYS_guarded_close_np) - (syscall-number SYS_guarded_open_np) - (syscall-number SYS_guarded_pwrite_np) - (syscall-number SYS_kdebug_trace) - (syscall-number SYS_kdebug_typefilter) - (syscall-number SYS_listxattr) - (syscall-number SYS_lseek) - (syscall-number SYS_memorystatus_control) - (syscall-number SYS_mkdir) - (syscall-number SYS_mkdirat) - (syscall-number SYS_mlock) - (syscall-number SYS_msync) - (syscall-number SYS_munlock) - (syscall-number SYS_necp_client_action) - (syscall-number SYS_necp_open) - (syscall-number SYS_openat) - (syscall-number SYS_openat_nocancel) - (syscall-number SYS_pathconf) - (syscall-number SYS_pipe) - (syscall-number SYS_pread_nocancel) - (syscall-number SYS_proc_rlimit_control) - (syscall-number SYS_process_policy) - (syscall-number SYS_psynch_cvbroad) - (syscall-number SYS_psynch_cvclrprepost) - (syscall-number SYS_psynch_cvsignal) - (syscall-number SYS_psynch_cvwait) - (syscall-number SYS_psynch_rw_unlock) - (syscall-number SYS_psynch_rw_wrlock) - (syscall-number SYS_pwrite) - (syscall-number SYS_quotactl) - (syscall-number SYS_recvfrom_nocancel) - (syscall-number SYS_rename) - (syscall-number SYS_rmdir) - (syscall-number SYS_select) - (syscall-number SYS_select_nocancel) - (syscall-number SYS_sem_close) - (syscall-number SYS_sem_open) - (syscall-number SYS_sem_post) - (syscall-number SYS_sem_wait) - (syscall-number SYS_sendmsg_nocancel) - (syscall-number SYS_sendto) - (syscall-number SYS_sendto_nocancel) - (syscall-number SYS_setpriority) - (syscall-number SYS_setrlimit) - (syscall-number SYS_setsockopt) - (syscall-number SYS_shared_region_check_np) - (syscall-number SYS_shutdown) - (syscall-number SYS_sigaltstack) - (syscall-number SYS_umask) - (syscall-number SYS_unlink) - (syscall-number SYS_work_interval_ctl) - (syscall-number SYS_write) - (syscall-number SYS_write_nocancel) - (syscall-number SYS_writev) -))) diff --git a/chromium/services/service_manager/sandbox/mac/sandbox_mac.h b/chromium/services/service_manager/sandbox/mac/sandbox_mac.h deleted file mode 100644 index dd2d9e1b1a4..00000000000 --- a/chromium/services/service_manager/sandbox/mac/sandbox_mac.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICE_MANAGER_SANDBOX_MAC_SANDBOX_MAC_H_ -#define SERVICE_MANAGER_SANDBOX_MAC_SANDBOX_MAC_H_ - -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/sandbox_type.h" - -namespace base { -class FilePath; -} - -namespace service_manager { - -class SERVICE_MANAGER_SANDBOX_EXPORT SandboxMac { - public: - // Warm up System APIs that empirically need to be accessed before the - // sandbox is turned on. |sandbox_type| is the type of sandbox to warm up. - // Valid |sandbox_type| values are defined by the enum SandboxType, or can be - // defined by the embedder via - // ContentClient::GetSandboxProfileForProcessType(). - static void Warmup(SandboxType sandbox_type); - - // Turns on the OS X sandbox for this process. - // |sandbox_type| - type of Sandbox to use. See SandboxWarmup() for legal - // values. - // - // Returns true on success, false if an error occurred enabling the sandbox. - static bool Enable(SandboxType sandbox_type); - - // Convert provided path into a "canonical" path matching what the Sandbox - // expects i.e. one without symlinks. - // This path is not necessarily unique e.g. in the face of hardlinks. - static base::FilePath GetCanonicalPath(const base::FilePath& path); - - // Returns the sandbox profile string for a given sandbox type. - // It CHECKs that the sandbox profile is a valid type, so it always returns a - // valid result, or crashes. - static std::string GetSandboxProfile(SandboxType sandbox_type); - - static const char* kSandboxBrowserPID; - static const char* kSandboxBundlePath; - static const char* kSandboxChromeBundleId; - static const char* kSandboxComponentPath; - static const char* kSandboxDisableDenialLogging; - static const char* kSandboxEnableLogging; - static const char* kSandboxHomedirAsLiteral; - static const char* kSandboxLoggingPathAsLiteral; - static const char* kSandboxOSVersion; - - // TODO(kerrnel): this is only for the legacy sandbox. - static const char* kSandboxElCapOrLater; - static const char* kSandboxMacOS1013; - static const char* kSandboxFieldTrialSeverName; - - static const char* kSandboxBundleVersionPath; - - private: - FRIEND_TEST_ALL_PREFIXES(MacDirAccessSandboxTest, StringEscape); - FRIEND_TEST_ALL_PREFIXES(MacDirAccessSandboxTest, RegexEscape); - FRIEND_TEST_ALL_PREFIXES(MacDirAccessSandboxTest, SandboxAccess); - - DISALLOW_IMPLICIT_CONSTRUCTORS(SandboxMac); -}; - -} // namespace service_manager - -#endif // SERVICE_MANAGER_SANDBOX_MAC_SANDBOX_MAC_H_ diff --git a/chromium/services/service_manager/sandbox/mac/sandbox_mac.mm b/chromium/services/service_manager/sandbox/mac/sandbox_mac.mm deleted file mode 100644 index 511d7edb0c2..00000000000 --- a/chromium/services/service_manager/sandbox/mac/sandbox_mac.mm +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/mac/sandbox_mac.h" - -#import <Cocoa/Cocoa.h> -#include <stddef.h> -#include <stdint.h> - -#include <CoreFoundation/CFTimeZone.h> -#include <signal.h> -#include <sys/param.h> - -#include <algorithm> -#include <iterator> -#include <map> -#include <string> - -#include "base/command_line.h" -#include "base/compiler_specific.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/mac/bundle_locations.h" -#include "base/mac/foundation_util.h" -#include "base/mac/mac_util.h" -#include "base/mac/mach_port_rendezvous.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/mac/scoped_nsobject.h" -#include "base/rand_util.h" -#include "base/stl_util.h" -#include "base/strings/string16.h" -#include "base/strings/string_piece.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/sys_string_conversions.h" -#include "base/strings/utf_string_conversions.h" -#include "base/system/sys_info.h" -#include "sandbox/mac/sandbox_compiler.h" -#include "services/service_manager/sandbox/mac/audio.sb.h" -#include "services/service_manager/sandbox/mac/cdm.sb.h" -#include "services/service_manager/sandbox/mac/common.sb.h" -#include "services/service_manager/sandbox/mac/gpu.sb.h" -#include "services/service_manager/sandbox/mac/gpu_v2.sb.h" -#include "services/service_manager/sandbox/mac/nacl_loader.sb.h" -#include "services/service_manager/sandbox/mac/network.sb.h" -#include "services/service_manager/sandbox/mac/ppapi.sb.h" -#include "services/service_manager/sandbox/mac/print_compositor.sb.h" -#include "services/service_manager/sandbox/mac/renderer.sb.h" -#include "services/service_manager/sandbox/mac/utility.sb.h" -#include "services/service_manager/sandbox/sandbox_type.h" -#include "services/service_manager/sandbox/switches.h" - -namespace service_manager { - -// Static variable declarations. -const char* SandboxMac::kSandboxBrowserPID = "BROWSER_PID"; -const char* SandboxMac::kSandboxBundlePath = "BUNDLE_PATH"; -const char* SandboxMac::kSandboxChromeBundleId = "BUNDLE_ID"; -const char* SandboxMac::kSandboxComponentPath = "COMPONENT_PATH"; -const char* SandboxMac::kSandboxDisableDenialLogging = - "DISABLE_SANDBOX_DENIAL_LOGGING"; -const char* SandboxMac::kSandboxEnableLogging = "ENABLE_LOGGING"; -const char* SandboxMac::kSandboxHomedirAsLiteral = "USER_HOMEDIR_AS_LITERAL"; -const char* SandboxMac::kSandboxLoggingPathAsLiteral = "LOG_FILE_PATH"; -const char* SandboxMac::kSandboxOSVersion = "OS_VERSION"; -const char* SandboxMac::kSandboxElCapOrLater = "ELCAP_OR_LATER"; -const char* SandboxMac::kSandboxMacOS1013 = "MACOS_1013"; -const char* SandboxMac::kSandboxFieldTrialSeverName = "FIELD_TRIAL_SERVER_NAME"; -const char* SandboxMac::kSandboxBundleVersionPath = "BUNDLE_VERSION_PATH"; - -// Warm up System APIs that empirically need to be accessed before the Sandbox -// is turned on. -// This method is layed out in blocks, each one containing a separate function -// that needs to be warmed up. The OS version on which we found the need to -// enable the function is also noted. -// This function is tested on the following OS versions: -// 10.5.6, 10.6.0 - -// static -void SandboxMac::Warmup(SandboxType sandbox_type) { - DCHECK_EQ(sandbox_type, SandboxType::kGpu); - - @autoreleasepool { - { // CGColorSpaceCreateWithName(), CGBitmapContextCreate() - 10.5.6 - base::ScopedCFTypeRef<CGColorSpaceRef> rgb_colorspace( - CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)); - - // Allocate a 1x1 image. - char data[4]; - base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate( - data, 1, 1, 8, 1 * 4, rgb_colorspace, - kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); - - // Load in the color profiles we'll need (as a side effect). - ignore_result(base::mac::GetSRGBColorSpace()); - ignore_result(base::mac::GetSystemColorSpace()); - - // CGColorSpaceCreateSystemDefaultCMYK - 10.6 - base::ScopedCFTypeRef<CGColorSpaceRef> cmyk_colorspace( - CGColorSpaceCreateWithName(kCGColorSpaceGenericCMYK)); - } - - { // localtime() - 10.5.6 - time_t tv = {0}; - localtime(&tv); - } - - { // Gestalt() tries to read - // /System/Library/CoreServices/SystemVersion.plist - // on 10.5.6 - int32_t tmp; - base::SysInfo::OperatingSystemVersionNumbers(&tmp, &tmp, &tmp); - } - - { // CGImageSourceGetStatus() - 10.6 - // Create a png with just enough data to get everything warmed up... - char png_header[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; - NSData* data = [NSData dataWithBytes:png_header - length:base::size(png_header)]; - base::ScopedCFTypeRef<CGImageSourceRef> img( - CGImageSourceCreateWithData((CFDataRef)data, NULL)); - CGImageSourceGetStatus(img); - } - - { - // Allow access to /dev/urandom. - base::GetUrandomFD(); - } - - { // IOSurfaceLookup() - 10.7 - // Needed by zero-copy texture update framework - crbug.com/323338 - base::ScopedCFTypeRef<IOSurfaceRef> io_surface(IOSurfaceLookup(0)); - } - } -} - -// Load the appropriate template for the given sandbox type. -// Returns the template as a string or an empty string on error. -std::string LoadSandboxTemplate(SandboxType sandbox_type) { - DCHECK_EQ(sandbox_type, SandboxType::kGpu); - return kSeatbeltPolicyString_gpu; -} - -// Turns on the OS X sandbox for this process. - -// static -bool SandboxMac::Enable(SandboxType sandbox_type) { - DCHECK_EQ(sandbox_type, SandboxType::kGpu); - - std::string sandbox_data = LoadSandboxTemplate(sandbox_type); - if (sandbox_data.empty()) - return false; - - sandbox::SandboxCompiler compiler(sandbox_data); - - // Enable verbose logging if enabled on the command line. (See common.sb - // for details). - const base::CommandLine* command_line = - base::CommandLine::ForCurrentProcess(); - bool enable_logging = - command_line->HasSwitch(switches::kEnableSandboxLogging); - if (!compiler.InsertBooleanParam(kSandboxEnableLogging, enable_logging)) - return false; - - // Without this, the sandbox will print a message to the system log every - // time it denies a request. This floods the console with useless spew. - if (!compiler.InsertBooleanParam(kSandboxDisableDenialLogging, - !enable_logging)) - return false; - - // Splice the path of the user's home directory into the sandbox profile - // (see renderer.sb for details). - std::string home_dir = [NSHomeDirectory() fileSystemRepresentation]; - base::FilePath home_dir_canonical = - GetCanonicalPath(base::FilePath(home_dir)); - - if (!compiler.InsertStringParam(kSandboxHomedirAsLiteral, - home_dir_canonical.value())) { - return false; - } - - if (!compiler.InsertStringParam( - kSandboxFieldTrialSeverName, - base::MachPortRendezvousClient::GetBootstrapName())) { - return false; - } - - bool elcap_or_later = base::mac::IsAtLeastOS10_11(); - if (!compiler.InsertBooleanParam(kSandboxElCapOrLater, elcap_or_later)) - return false; - - bool macos_1013 = base::mac::IsOS10_13(); - if (!compiler.InsertBooleanParam(kSandboxMacOS1013, macos_1013)) - return false; - - if (sandbox_type == service_manager::SandboxType::kGpu) { - base::FilePath bundle_path = - SandboxMac::GetCanonicalPath(base::mac::FrameworkBundlePath()); - if (!compiler.InsertStringParam(kSandboxBundleVersionPath, - bundle_path.value())) - return false; - } - - // Initialize sandbox. - std::string error_str; - bool success = compiler.CompileAndApplyProfile(&error_str); - DLOG_IF(FATAL, !success) << "Failed to initialize sandbox: " << error_str; - return success; -} - -// static -base::FilePath SandboxMac::GetCanonicalPath(const base::FilePath& path) { - base::ScopedFD fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); - if (!fd.is_valid()) { - DPLOG(ERROR) << "GetCanonicalSandboxPath() failed for: " << path.value(); - return path; - } - - base::FilePath::CharType canonical_path[MAXPATHLEN]; - if (HANDLE_EINTR(fcntl(fd.get(), F_GETPATH, canonical_path)) != 0) { - DPLOG(ERROR) << "GetCanonicalSandboxPath() failed for: " << path.value(); - return path; - } - - return base::FilePath(canonical_path); -} - -// static -std::string SandboxMac::GetSandboxProfile(SandboxType sandbox_type) { - std::string profile = - std::string(service_manager::kSeatbeltPolicyString_common); - - switch (sandbox_type) { - case service_manager::SandboxType::kAudio: - profile += service_manager::kSeatbeltPolicyString_audio; - break; - case service_manager::SandboxType::kCdm: - profile += service_manager::kSeatbeltPolicyString_cdm; - break; - case service_manager::SandboxType::kGpu: - profile += service_manager::kSeatbeltPolicyString_gpu_v2; - break; - case service_manager::SandboxType::kNaClLoader: - profile += service_manager::kSeatbeltPolicyString_nacl_loader; - break; - case service_manager::SandboxType::kNetwork: - profile += service_manager::kSeatbeltPolicyString_network; - break; - case service_manager::SandboxType::kPpapi: - profile += service_manager::kSeatbeltPolicyString_ppapi; - break; - case service_manager::SandboxType::kPrintCompositor: - profile += service_manager::kSeatbeltPolicyString_print_compositor; - break; - case service_manager::SandboxType::kUtility: - profile += service_manager::kSeatbeltPolicyString_utility; - break; - case service_manager::SandboxType::kRenderer: - profile += service_manager::kSeatbeltPolicyString_renderer; - break; - case service_manager::SandboxType::kNoSandbox: - case service_manager::SandboxType::kVideoCapture: - case service_manager::SandboxType::kSpeechRecognition: - CHECK(false); - break; - } - return profile; -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/mac/utility.sb b/chromium/services/service_manager/sandbox/mac/utility.sb deleted file mode 100644 index c71837a0ca7..00000000000 --- a/chromium/services/service_manager/sandbox/mac/utility.sb +++ /dev/null @@ -1,26 +0,0 @@ -;; -;; Copyright (c) 2011 The Chromium Authors. All rights reserved. -;; Use of this source code is governed by a BSD-style license that can be -;; found in the LICENSE file. -;; -; This is the Sandbox configuration file used for safeguarding the utility -; process which is used for performing sandboxed operations that need to touch -; the filesystem like decoding theme images and unpacking extensions. -; -; This configuration locks everything down, except access to one configurable -; directory. This is different from other sandbox configuration files where -; file system access is entireley restricted. - -; *** The contents of common.sb are implicitly included here. *** - -; No additional resource access needed. - -; This is available in 10.15+, and rolled out as a Finch experiment. -(if (param-true? filter-syscalls-debug) - (when (defined? 'syscall-unix) - (deny syscall-unix (with send-signal SIGSYS)) - (allow syscall-unix - (syscall-number SYS_psynch_cvwait) - (syscall-number SYS_sendto) - (syscall-number SYS_socketpair) -))) diff --git a/chromium/services/service_manager/sandbox/sandbox.cc b/chromium/services/service_manager/sandbox/sandbox.cc deleted file mode 100644 index c1af5077d19..00000000000 --- a/chromium/services/service_manager/sandbox/sandbox.cc +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/sandbox.h" - -#include "base/command_line.h" -#include "build/build_config.h" -#include "services/service_manager/sandbox/switches.h" - -#if defined(OS_ANDROID) -#include "base/android/jni_android.h" -#endif // defined(OS_ANDROID) - -#if defined(OS_LINUX) -#include "services/service_manager/sandbox/linux/sandbox_linux.h" -#endif // defined(OS_LINUX) - -#if defined(OS_MACOSX) -#include "sandbox/mac/seatbelt.h" -#include "services/service_manager/sandbox/mac/sandbox_mac.h" -#endif // defined(OS_MACOSX) - -#if defined(OS_WIN) -#include "base/process/process_info.h" -#include "sandbox/win/src/sandbox.h" -#include "services/service_manager/sandbox/win/sandbox_win.h" -#endif // defined(OS_WIN) - -namespace service_manager { - -#if defined(OS_LINUX) -bool Sandbox::Initialize(SandboxType sandbox_type, - SandboxLinux::PreSandboxHook hook, - const SandboxLinux::Options& options) { - return SandboxLinux::GetInstance()->InitializeSandbox( - sandbox_type, std::move(hook), options); -} -#endif // defined(OS_LINUX) - -#if defined(OS_MACOSX) -bool Sandbox::Initialize(SandboxType sandbox_type, base::OnceClosure hook) { - // Warm up APIs before turning on the sandbox. - SandboxMac::Warmup(sandbox_type); - - // Execute the post warmup callback. - if (!hook.is_null()) - std::move(hook).Run(); - - // Actually sandbox the process. - return SandboxMac::Enable(sandbox_type); -} -#endif // defined(OS_MACOSX) - -#if defined(OS_WIN) -bool Sandbox::Initialize(SandboxType sandbox_type, - sandbox::SandboxInterfaceInfo* sandbox_info) { - sandbox::BrokerServices* broker_services = sandbox_info->broker_services; - if (broker_services) { - if (!SandboxWin::InitBrokerServices(broker_services)) - return false; - - // IMPORTANT: This piece of code needs to run as early as possible in the - // process because it will initialize the sandbox broker, which requires the - // process to swap its window station. During this time all the UI will be - // broken. This has to run before threads and windows are created. - if (!IsUnsandboxedSandboxType(sandbox_type)) { - // Precreate the desktop and window station used by the renderers. - scoped_refptr<sandbox::TargetPolicy> policy = - broker_services->CreatePolicy(); - sandbox::ResultCode result = policy->CreateAlternateDesktop(true); - CHECK(sandbox::SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION != result); - } - return true; - } - return IsUnsandboxedSandboxType(sandbox_type) || - SandboxWin::InitTargetServices(sandbox_info->target_services); -} -#endif // defined(OS_WIN) - -// static -bool Sandbox::IsProcessSandboxed() { - auto* command_line = base::CommandLine::ForCurrentProcess(); - bool is_browser = !command_line->HasSwitch(switches::kProcessType); - - if (!is_browser && - base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoSandbox)) { - // When running with --no-sandbox, unconditionally report the process as - // sandboxed. This lets code write |DCHECK(IsProcessSandboxed())| and not - // break when testing with the --no-sandbox switch. - return true; - } - -#if defined(OS_ANDROID) - // Note that this does not check the status of the Seccomp sandbox. Call - // https://developer.android.com/reference/android/os/Process#isIsolated(). - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::ScopedJavaLocalRef<jclass> process_class = - base::android::GetClass(env, "android/os/Process"); - jmethodID is_isolated = - base::android::MethodID::Get<base::android::MethodID::TYPE_STATIC>( - env, process_class.obj(), "isIsolated", "()Z"); - return env->CallStaticBooleanMethod(process_class.obj(), is_isolated); -#elif defined(OS_FUCHSIA) - // TODO(https://crbug.com/1071420): Figure out what to do here. Process - // launching controls the sandbox and there are no ambient capabilities, so - // basically everything but the browser is considered sandboxed. - return !is_browser; -#elif defined(OS_LINUX) - int status = SandboxLinux::GetInstance()->GetStatus(); - constexpr int kLayer1Flags = SandboxLinux::Status::kSUID | - SandboxLinux::Status::kPIDNS | - SandboxLinux::Status::kUserNS; - constexpr int kLayer2Flags = - SandboxLinux::Status::kSeccompBPF | SandboxLinux::Status::kSeccompTSYNC; - return (status & kLayer1Flags) != 0 && (status & kLayer2Flags) != 0; -#elif defined(OS_MACOSX) - return sandbox::Seatbelt::IsSandboxed(); -#elif defined(OS_WIN) - return base::GetCurrentProcessIntegrityLevel() < base::MEDIUM_INTEGRITY; -#else - return false; -#endif -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/sandbox.h b/chromium/services/service_manager/sandbox/sandbox.h deleted file mode 100644 index 535df983713..00000000000 --- a/chromium/services/service_manager/sandbox/sandbox.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICE_MANAGER_SANDBOX_SANDBOX_H_ -#define SERVICE_MANAGER_SANDBOX_SANDBOX_H_ - -#include "build/build_config.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/sandbox_type.h" - -#if defined(OS_LINUX) -#include "services/service_manager/sandbox/linux/sandbox_linux.h" -#endif - -#if defined(OS_MACOSX) -#include "base/callback.h" -#endif // defined(OS_MACOSX) - -namespace sandbox { -struct SandboxInterfaceInfo; -} // namespace sandbox - -namespace service_manager { - -// Interface to the service manager sandboxes across the various platforms. -// -// Ideally, this API would abstract away the platform differences, but there -// are some major OS differences that shape this interface, including: -// * Whether the sandboxing is performed by the launcher (Windows, Fuchsia -// someday) or by the launchee (Linux, Mac). -// * The means of specifying the additional resources that are permitted. -// * The need to "warmup" other resources before engaing the sandbox. - -class SERVICE_MANAGER_SANDBOX_EXPORT Sandbox { - public: -#if defined(OS_LINUX) - static bool Initialize(SandboxType sandbox_type, - SandboxLinux::PreSandboxHook hook, - const SandboxLinux::Options& options); -#endif // defined(OS_LINUX) - -#if defined(OS_MACOSX) - // Initialize the sandbox of |sandbox_type|. Runs |post_warmup_hook| if - // non-empty after performing any sandbox warmup but immediately before - // engaging the sandbox. Return true on success, false otherwise. - static bool Initialize(SandboxType sandbox_type, - base::OnceClosure post_warmup_hook); -#endif // defined(OS_MACOSX) - -#if defined(OS_WIN) - static bool Initialize(service_manager::SandboxType sandbox_type, - sandbox::SandboxInterfaceInfo* sandbox_info); -#endif // defined(OS_WIN) - - // Returns true if the current process is running with a sandbox, and false - // if the process is not sandboxed. This should be used to assert that code is - // not running at high-privilege (e.g. in the browser process): - // - // DCHECK(service_manager::Sandbox::IsProcessSandboxed()); - // - // The definition of what constitutes a sandbox, and the relative strength of - // the restrictions placed on the process, and a per-platform implementation - // detail. - // - // Except if the process is the browser, if the process is running with the - // --no-sandbox flag, this unconditionally returns true. - static bool IsProcessSandboxed(); -}; - -} // namespace service_manager - -#endif // SERVICE_MANAGER_SANDBOX_SANDBOX_H_ diff --git a/chromium/services/service_manager/sandbox/sandbox_delegate.h b/chromium/services/service_manager/sandbox/sandbox_delegate.h deleted file mode 100644 index 9d32a52ced9..00000000000 --- a/chromium/services/service_manager/sandbox/sandbox_delegate.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_SANDBOX_DELEGATE_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_SANDBOX_DELEGATE_H_ - -#include <string> - -#include "base/process/process.h" -#include "build/build_config.h" -#include "services/service_manager/sandbox/sandbox_type.h" - -namespace sandbox { -class TargetPolicy; -} - -namespace service_manager { - -class SandboxDelegate { - public: - virtual ~SandboxDelegate() {} - - // Returns the SandboxType to enforce on the process, or - // SandboxType::kNoSandbox to run without a sandbox policy. - virtual service_manager::SandboxType GetSandboxType() = 0; - -#if defined(OS_WIN) - // Whether to disable the default policy specified in - // AddPolicyForSandboxedProcess. - virtual bool DisableDefaultPolicy() = 0; - - // Get the AppContainer ID for the sandbox. If this returns false then the - // AppContainer will not be enabled for the process. - virtual bool GetAppContainerId(std::string* appcontainer_id) = 0; - - // Called right before spawning the process. Returns false on failure. - virtual bool PreSpawnTarget(sandbox::TargetPolicy* policy) = 0; - - // Called right after the process is launched, but before its thread is run. - virtual void PostSpawnTarget(base::ProcessHandle process) = 0; -#endif // defined(OS_WIN) -}; - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_SANDBOX_DELEGATE_H_ diff --git a/chromium/services/service_manager/sandbox/sandbox_type.cc b/chromium/services/service_manager/sandbox/sandbox_type.cc deleted file mode 100644 index de79b63b7de..00000000000 --- a/chromium/services/service_manager/sandbox/sandbox_type.cc +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/sandbox_type.h" - -#include <string> - -#include "base/check.h" -#include "base/feature_list.h" -#include "base/logging.h" -#include "base/notreached.h" -#include "services/service_manager/sandbox/features.h" -#include "services/service_manager/sandbox/switches.h" - -namespace service_manager { - -bool IsUnsandboxedSandboxType(SandboxType sandbox_type) { - switch (sandbox_type) { - case SandboxType::kNoSandbox: - return true; -#if defined(OS_WIN) - case SandboxType::kNoSandboxAndElevatedPrivileges: - return true; - case SandboxType::kXrCompositing: - return !base::FeatureList::IsEnabled( - service_manager::features::kXRSandbox); - case SandboxType::kProxyResolver: - case SandboxType::kPdfConversion: - case SandboxType::kIconReader: - return false; -#endif - case SandboxType::kAudio: - return !IsAudioSandboxEnabled(); - case SandboxType::kVideoCapture: -#if defined(OS_FUCHSIA) - return false; -#else - return true; -#endif - case SandboxType::kNetwork: -#if defined(OS_MACOSX) - return false; -#else - return !base::FeatureList::IsEnabled( - service_manager::features::kNetworkServiceSandbox); -#endif // defined(OS_MACOSX) - case SandboxType::kRenderer: - case SandboxType::kUtility: - case SandboxType::kGpu: - case SandboxType::kPpapi: - case SandboxType::kCdm: - case SandboxType::kPrintCompositor: -#if defined(OS_FUCHSIA) - case SandboxType::kWebContext: -#endif -#if defined(OS_MACOSX) - case SandboxType::kNaClLoader: -#endif -#if defined(OS_CHROMEOS) - case SandboxType::kIme: - case SandboxType::kTts: -#endif -#if !defined(OS_MACOSX) - case SandboxType::kSharingService: -#endif -#if defined(OS_LINUX) - case SandboxType::kZygoteIntermediateSandbox: -#endif - case SandboxType::kSpeechRecognition: - return false; - } -} - -void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line, - SandboxType sandbox_type) { - switch (sandbox_type) { - case SandboxType::kNoSandbox: - if (command_line->GetSwitchValueASCII(switches::kProcessType) == - switches::kUtilityProcess) { - DCHECK(!command_line->HasSwitch(switches::kServiceSandboxType)); - command_line->AppendSwitchASCII( - switches::kServiceSandboxType, - StringFromUtilitySandboxType(sandbox_type)); - } else { - command_line->AppendSwitch(switches::kNoSandbox); - } - break; -#if defined(OS_WIN) - case SandboxType::kNoSandboxAndElevatedPrivileges: - command_line->AppendSwitch(switches::kNoSandboxAndElevatedPrivileges); - break; -#endif - case SandboxType::kRenderer: - DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) == - switches::kRendererProcess); - break; - case SandboxType::kGpu: - DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) == - switches::kGpuProcess); - break; - case SandboxType::kPpapi: - if (command_line->GetSwitchValueASCII(switches::kProcessType) == - switches::kUtilityProcess) { - command_line->AppendSwitchASCII(switches::kServiceSandboxType, - switches::kPpapiSandbox); - } else { - DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) == - switches::kPpapiPluginProcess); - } - break; - case SandboxType::kUtility: - case SandboxType::kNetwork: - case SandboxType::kCdm: - case SandboxType::kPrintCompositor: - case SandboxType::kAudio: - case SandboxType::kVideoCapture: -#if defined(OS_WIN) - case SandboxType::kXrCompositing: - case SandboxType::kProxyResolver: - case SandboxType::kPdfConversion: - case SandboxType::kIconReader: -#endif // defined(OS_WIN) -#if defined(OS_CHROMEOS) - case SandboxType::kIme: - case SandboxType::kTts: -#endif // defined(OS_CHROMEOS) -#if !defined(OS_MACOSX) - case SandboxType::kSharingService: -#endif - case SandboxType::kSpeechRecognition: - DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) == - switches::kUtilityProcess); - DCHECK(!command_line->HasSwitch(switches::kServiceSandboxType)); - command_line->AppendSwitchASCII( - switches::kServiceSandboxType, - StringFromUtilitySandboxType(sandbox_type)); - break; -#if defined(OS_FUCHSIA) - case SandboxType::kWebContext: - break; -#endif // defined(OS_FUCHSIA) -#if defined(OS_MACOSX) - case SandboxType::kNaClLoader: - break; -#endif // defined(OS_MACOSX) -#if defined(OS_LINUX) - case SandboxType::kZygoteIntermediateSandbox: - break; -#endif - } -} - -SandboxType SandboxTypeFromCommandLine(const base::CommandLine& command_line) { - if (command_line.HasSwitch(switches::kNoSandbox)) - return SandboxType::kNoSandbox; - -#if defined(OS_WIN) - if (command_line.HasSwitch(switches::kNoSandboxAndElevatedPrivileges)) - return SandboxType::kNoSandboxAndElevatedPrivileges; -#endif - - std::string process_type = - command_line.GetSwitchValueASCII(switches::kProcessType); - if (process_type.empty()) - return SandboxType::kNoSandbox; - - if (process_type == switches::kRendererProcess) - return SandboxType::kRenderer; - - if (process_type == switches::kUtilityProcess) { - return UtilitySandboxTypeFromString( - command_line.GetSwitchValueASCII(switches::kServiceSandboxType)); - } - if (process_type == switches::kGpuProcess) { - if (command_line.HasSwitch(switches::kDisableGpuSandbox)) - return SandboxType::kNoSandbox; - return SandboxType::kGpu; - } - if (process_type == switches::kPpapiBrokerProcess) - return SandboxType::kNoSandbox; - - if (process_type == switches::kPpapiPluginProcess) - return SandboxType::kPpapi; - - // NaCl tests on all platforms use the loader process. - if (process_type == switches::kNaClLoaderProcess) { -#if defined(OS_MACOSX) - return SandboxType::kNaClLoader; -#else - return SandboxType::kUtility; -#endif - } - - if (process_type == switches::kNaClBrokerProcess) - return SandboxType::kNoSandbox; - -#if defined(OS_LINUX) - // Intermediate process gains a sandbox later. - if (process_type == switches::kZygoteProcessType) - return SandboxType::kZygoteIntermediateSandbox; -#endif - - if (process_type == switches::kCloudPrintServiceProcess) - return SandboxType::kNoSandbox; - - CHECK(false) - << "Command line does not provide a valid sandbox configuration: " - << command_line.GetCommandLineString(); - NOTREACHED(); - return SandboxType::kNoSandbox; -} - -std::string StringFromUtilitySandboxType(SandboxType sandbox_type) { - switch (sandbox_type) { - case SandboxType::kNoSandbox: - return switches::kNoneSandbox; - case SandboxType::kNetwork: - return switches::kNetworkSandbox; - case SandboxType::kPpapi: - return switches::kPpapiSandbox; - case SandboxType::kCdm: - return switches::kCdmSandbox; - case SandboxType::kPrintCompositor: - return switches::kPrintCompositorSandbox; - case SandboxType::kUtility: - return switches::kUtilitySandbox; - case SandboxType::kAudio: - return switches::kAudioSandbox; - case SandboxType::kVideoCapture: - return switches::kVideoCaptureSandbox; -#if !defined(OS_MACOSX) - case SandboxType::kSharingService: - return switches::kSharingServiceSandbox; -#endif - case SandboxType::kSpeechRecognition: - return switches::kSpeechRecognitionSandbox; -#if defined(OS_WIN) - case SandboxType::kXrCompositing: - return switches::kXrCompositingSandbox; - case SandboxType::kProxyResolver: - return switches::kProxyResolverSandbox; - case SandboxType::kPdfConversion: - return switches::kPdfConversionSandbox; - case SandboxType::kIconReader: - return switches::kIconReaderSandbox; -#endif // defined(OS_WIN) -#if defined(OS_CHROMEOS) - case SandboxType::kIme: - return switches::kImeSandbox; - case SandboxType::kTts: - return switches::kTtsSandbox; -#endif // defined(OS_CHROMEOS) - // The following are not utility processes so should not occur. - case SandboxType::kRenderer: - case SandboxType::kGpu: -#if defined(OS_WIN) - case SandboxType::kNoSandboxAndElevatedPrivileges: -#endif // defined(OS_WIN) -#if defined(OS_MACOSX) - case SandboxType::kNaClLoader: -#endif // defined(OS_MACOSX) -#if defined(OS_FUCHSIA) - case SandboxType::kWebContext: -#endif // defined(OS_FUCHSIA) -#if defined(OS_LINUX) - case SandboxType::kZygoteIntermediateSandbox: -#endif - NOTREACHED(); - return std::string(); - } -} - -SandboxType UtilitySandboxTypeFromString(const std::string& sandbox_string) { - if (sandbox_string == switches::kNoneSandbox) - return SandboxType::kNoSandbox; - if (sandbox_string == switches::kNoneSandboxAndElevatedPrivileges) { -#if defined(OS_WIN) - return SandboxType::kNoSandboxAndElevatedPrivileges; -#else - return SandboxType::kNoSandbox; -#endif - } - if (sandbox_string == switches::kNetworkSandbox) - return SandboxType::kNetwork; - if (sandbox_string == switches::kPpapiSandbox) - return SandboxType::kPpapi; - if (sandbox_string == switches::kCdmSandbox) - return SandboxType::kCdm; - if (sandbox_string == switches::kPrintCompositorSandbox) - return SandboxType::kPrintCompositor; -#if defined(OS_WIN) - if (sandbox_string == switches::kXrCompositingSandbox) - return SandboxType::kXrCompositing; - if (sandbox_string == switches::kProxyResolverSandbox) - return SandboxType::kProxyResolver; - if (sandbox_string == switches::kPdfConversionSandbox) - return SandboxType::kPdfConversion; - if (sandbox_string == switches::kIconReaderSandbox) - return SandboxType::kIconReader; -#endif - if (sandbox_string == switches::kAudioSandbox) - return SandboxType::kAudio; - if (sandbox_string == switches::kSpeechRecognitionSandbox) - return SandboxType::kSpeechRecognition; - if (sandbox_string == switches::kVideoCaptureSandbox) - return SandboxType::kVideoCapture; -#if defined(OS_CHROMEOS) - if (sandbox_string == switches::kImeSandbox) - return SandboxType::kIme; - if (sandbox_string == switches::kTtsSandbox) - return SandboxType::kTts; -#endif // defined(OS_CHROMEOS) - return SandboxType::kUtility; -} - -void EnableAudioSandbox(bool enable) { - if (enable) { - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kEnableAudioServiceSandbox); - } -} - -bool IsAudioSandboxEnabled() { - return base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableAudioServiceSandbox); -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/sandbox_type.h b/chromium/services/service_manager/sandbox/sandbox_type.h deleted file mode 100644 index b35e3950ccf..00000000000 --- a/chromium/services/service_manager/sandbox/sandbox_type.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_SANDBOX_TYPE_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_SANDBOX_TYPE_H_ - -#include <string> - -#include "base/command_line.h" -#include "build/build_config.h" -#include "services/service_manager/sandbox/export.h" - -namespace service_manager { - -// Defines the sandbox types known within the servicemanager. -enum class SandboxType { - // Do not apply any sandboxing to the process. - kNoSandbox, - -#if defined(OS_WIN) - // Do not apply any sandboxing and elevate the privileges of the process. - kNoSandboxAndElevatedPrivileges, - - // The XR Compositing process. - kXrCompositing, - - // The proxy resolver process. - kProxyResolver, - - // The PDF conversion service process used in printing. - kPdfConversion, - - // The icon reader service. - kIconReader, -#endif - -#if defined(OS_FUCHSIA) - // Sandbox type for the web::Context process on Fuchsia. Functionally it's an - // equivalent of the browser process on other platforms. - kWebContext, -#endif - - // Renderer or worker process. Most common case. - kRenderer, - - // Utility processes. Used by most isolated services. - kUtility, - - // GPU process. - kGpu, - - // The PPAPI plugin process. - kPpapi, - - // The network service process. - kNetwork, - - // The CDM service process. - kCdm, - -#if defined(OS_MACOSX) - // The NaCl loader process. - kNaClLoader, -#endif // defined(OS_MACOSX) - - // The print compositor service process. - kPrintCompositor, - - // The audio service process. - kAudio, - -#if defined(OS_CHROMEOS) - kIme, - // Text-to-speech. - kTts, -#endif // defined(OS_CHROMEOS) - -#if defined(OS_LINUX) - // Indicates that a process is a zygote and will get a real sandbox later. - kZygoteIntermediateSandbox, -#endif - -#if !defined(OS_MACOSX) - // Hosts WebRTC for Sharing Service, uses kUtility on OS_MACOSX. - kSharingService, -#endif - - // The speech recognition service process. - kSpeechRecognition, - - // Equivalent to no sandbox on all non-Fuchsia platforms. - // Minimally privileged sandbox on Fuchsia. - kVideoCapture, - - kMaxValue = kVideoCapture -}; - -SERVICE_MANAGER_SANDBOX_EXPORT bool IsUnsandboxedSandboxType( - SandboxType sandbox_type); - -SERVICE_MANAGER_SANDBOX_EXPORT void SetCommandLineFlagsForSandboxType( - base::CommandLine* command_line, - SandboxType sandbox_type); - -SERVICE_MANAGER_SANDBOX_EXPORT SandboxType -SandboxTypeFromCommandLine(const base::CommandLine& command_line); - -SERVICE_MANAGER_SANDBOX_EXPORT std::string StringFromUtilitySandboxType( - SandboxType sandbox_type); - -SERVICE_MANAGER_SANDBOX_EXPORT SandboxType -UtilitySandboxTypeFromString(const std::string& sandbox_string); - -SERVICE_MANAGER_SANDBOX_EXPORT void EnableAudioSandbox(bool enable); - -SERVICE_MANAGER_SANDBOX_EXPORT bool IsAudioSandboxEnabled(); - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_SANDBOX_TYPE_H_ diff --git a/chromium/services/service_manager/sandbox/switches.cc b/chromium/services/service_manager/sandbox/switches.cc deleted file mode 100644 index e635d4e8eed..00000000000 --- a/chromium/services/service_manager/sandbox/switches.cc +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/switches.h" - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include "base/command_line.h" -#include "base/win/windows_version.h" -#endif - -namespace service_manager { -namespace switches { - -// Type of sandbox to apply to the process running the service, one of the -// values in the next block. -const char kServiceSandboxType[] = "service-sandbox-type"; - -// Must be in sync with "sandbox_type" values as used in service manager's -// manifest.json catalog files. -const char kNoneSandbox[] = "none"; -const char kNoneSandboxAndElevatedPrivileges[] = "none_and_elevated"; -const char kNetworkSandbox[] = "network"; -const char kPpapiSandbox[] = "ppapi"; -const char kUtilitySandbox[] = "utility"; -const char kCdmSandbox[] = "cdm"; -const char kPrintCompositorSandbox[] = "print_compositor"; -const char kAudioSandbox[] = "audio"; -const char kSharingServiceSandbox[] = "sharing_service"; -const char kSpeechRecognitionSandbox[] = "speech_recognition"; -const char kVideoCaptureSandbox[] = "video_capture"; - -#if defined(OS_WIN) -const char kPdfConversionSandbox[] = "pdf_conversion"; -const char kProxyResolverSandbox[] = "proxy_resolver"; -const char kXrCompositingSandbox[] = "xr_compositing"; -const char kIconReaderSandbox[] = "icon_reader"; -#endif // OS_WIN - -#if defined(OS_CHROMEOS) -const char kImeSandbox[] = "ime"; -const char kTtsSandbox[] = "tts"; -#endif // OS_CHROMEOS - -// Flags owned by the service manager sandbox. - -// Enables the sandboxed processes to run without a job object assigned to them. -// This flag is required to allow Chrome to run in RemoteApps or Citrix. This -// flag can reduce the security of the sandboxed processes and allow them to do -// certain API calls like shut down Windows or access the clipboard. Also we -// lose the chance to kill some processes until the outer job that owns them -// finishes. -const char kAllowNoSandboxJob[] = "allow-no-sandbox-job"; - -// Allows debugging of sandboxed processes (see zygote_main_linux.cc). -const char kAllowSandboxDebugging[] = "allow-sandbox-debugging"; - -// Disables the GPU process sandbox. -const char kDisableGpuSandbox[] = "disable-gpu-sandbox"; - -// Disables usage of the namespace sandbox. -const char kDisableNamespaceSandbox[] = "disable-namespace-sandbox"; - -// Disable the seccomp filter sandbox (seccomp-bpf) (Linux only). -const char kDisableSeccompFilterSandbox[] = "disable-seccomp-filter-sandbox"; - -// Disable the setuid sandbox (Linux only). -const char kDisableSetuidSandbox[] = "disable-setuid-sandbox"; - -// Disables the Win32K process mitigation policy for child processes. -const char kDisableWin32kLockDown[] = "disable-win32k-lockdown"; - -// Command line flag to enable the audio service sandbox. -const char kEnableAudioServiceSandbox[] = "enable-audio-service-sandbox"; - -// Allows shmat() system call in the GPU sandbox. -const char kGpuSandboxAllowSysVShm[] = "gpu-sandbox-allow-sysv-shm"; - -// Makes GPU sandbox failures fatal. -const char kGpuSandboxFailuresFatal[] = "gpu-sandbox-failures-fatal"; - -// Disables the sandbox for all process types that are normally sandboxed. -// Meant to be used as a browser-level switch for testing purposes only. -const char kNoSandbox[] = "no-sandbox"; - -#if defined(OS_LINUX) -// Instructs the zygote to launch without a sandbox. Processes forked from this -// type of zygote will apply their own custom sandboxes later. -const char kNoZygoteSandbox[] = "no-zygote-sandbox"; -#endif - -#if defined(OS_WIN) -// Allows third party modules to inject by disabling the BINARY_SIGNATURE -// mitigation policy on Win10+. Also has other effects in ELF. -const char kAllowThirdPartyModules[] = "allow-third-party-modules"; - -// Add additional capabilities to the AppContainer sandbox on the GPU process. -const char kAddGpuAppContainerCaps[] = "add-gpu-appcontainer-caps"; - -// Disables the sandbox and gives the process elevated privileges. -const char kNoSandboxAndElevatedPrivileges[] = "no-sandbox-and-elevated"; - -// Add additional capabilities to the AppContainer sandbox used for XR -// compositing. -const char kAddXrAppContainerCaps[] = "add-xr-appcontainer-caps"; -#endif - -#if defined(OS_MACOSX) -// Cause the OS X sandbox write to syslog every time an access to a resource -// is denied by the sandbox. -const char kEnableSandboxLogging[] = "enable-sandbox-logging"; -#endif - -// Flags spied upon from other layers. -const char kGpuProcess[] = "gpu-process"; -const char kNaClBrokerProcess[] = "nacl-broker"; -const char kNaClLoaderProcess[] = "nacl-loader"; -const char kPpapiBrokerProcess[] = "ppapi-broker"; -const char kPpapiPluginProcess[] = "ppapi"; -const char kRendererProcess[] = "renderer"; -const char kUtilityProcess[] = "utility"; -const char kCloudPrintServiceProcess[] = "service"; -const char kZygoteProcessType[] = "zygote"; - -} // namespace switches - -#if defined(OS_WIN) - -bool IsWin32kLockdownEnabled() { - return base::win::GetVersion() >= base::win::Version::WIN8 && - !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableWin32kLockDown); -} - -#endif - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/switches.h b/chromium/services/service_manager/sandbox/switches.h deleted file mode 100644 index 6d66ab6c4f2..00000000000 --- a/chromium/services/service_manager/sandbox/switches.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_SWITCHES_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_SWITCHES_H_ - -#include "build/build_config.h" -#include "services/service_manager/embedder/switches.h" -#include "services/service_manager/sandbox/export.h" - -namespace service_manager { -namespace switches { - -// Type of sandbox to apply to the process running the service, one of the -// values in the next block. -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kServiceSandboxType[]; - -// Must be in sync with "sandbox_type" values as used in service manager's -// manifest.json catalog files. -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kNoneSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char - kNoneSandboxAndElevatedPrivileges[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kNetworkSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kPpapiSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kUtilitySandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kCdmSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kPrintCompositorSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kAudioSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kSharingServiceSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kSpeechRecognitionSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kVideoCaptureSandbox[]; - -#if defined(OS_WIN) -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kPdfConversionSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kProxyResolverSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kXrCompositingSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kIconReaderSandbox[]; -#endif // OS_WIN - -#if defined(OS_CHROMEOS) -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kImeSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kTtsSandbox[]; -#endif // OS_CHROMEOS - -// Flags owned by the service manager sandbox. -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kAllowNoSandboxJob[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kAllowSandboxDebugging[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kDisableGpuSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kDisableNamespaceSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kDisableSeccompFilterSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kDisableSetuidSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kDisableWin32kLockDown[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kEnableAudioServiceSandbox[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kGpuSandboxAllowSysVShm[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kGpuSandboxFailuresFatal[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kNoSandbox[]; -#if defined(OS_LINUX) -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kNoZygoteSandbox[]; -#endif -#if defined(OS_WIN) -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kAllowThirdPartyModules[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kAddGpuAppContainerCaps[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char - kNoSandboxAndElevatedPrivileges[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kAddXrAppContainerCaps[]; -#endif -#if defined(OS_MACOSX) -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kEnableSandboxLogging[]; -#endif - -// Flags spied upon from other layers. -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kGpuProcess[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kNaClBrokerProcess[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kNaClLoaderProcess[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kPpapiBrokerProcess[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kPpapiPluginProcess[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kRendererProcess[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kUtilityProcess[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kCloudPrintServiceProcess[]; -SERVICE_MANAGER_SANDBOX_EXPORT extern const char kZygoteProcessType[]; - -} // namespace switches - -#if defined(OS_WIN) -// Returns whether Win32k lockdown is enabled for child processes or not. -// Not really a switch, but uses one under the covers. -SERVICE_MANAGER_SANDBOX_EXPORT bool IsWin32kLockdownEnabled(); -#endif - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_SWITCHES_H_ diff --git a/chromium/services/service_manager/sandbox/win/OWNERS b/chromium/services/service_manager/sandbox/win/OWNERS deleted file mode 100644 index b875dc5b309..00000000000 --- a/chromium/services/service_manager/sandbox/win/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -set noparent -file://sandbox/win/OWNERS diff --git a/chromium/services/service_manager/sandbox/win/sandbox_diagnostics.cc b/chromium/services/service_manager/sandbox/win/sandbox_diagnostics.cc deleted file mode 100644 index 140a9b1b53c..00000000000 --- a/chromium/services/service_manager/sandbox/win/sandbox_diagnostics.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/win/sandbox_diagnostics.h" - -#include <stddef.h> - -#include <string> -#include <utility> -#include <vector> - -#include "base/json/json_reader.h" -#include "base/values.h" - -namespace service_manager { -namespace { -// Runs on a non-sandbox thread to ensure that response callback is not -// invoked from sandbox process and job tracker thread, and that conversion -// work does not block process or job registration. Converts |policies| -// into base::Value form, then invokes |response| on the same sequence. -static void ConvertToValuesAndRespond( - std::unique_ptr<sandbox::PolicyList> policies, - base::OnceCallback<void(base::Value)> response) { - base::Value policy_values(base::Value::Type::LIST); - for (auto&& item : *policies) { - auto snapshot = base::JSONReader::ReadAndReturnValueWithError( - item->JsonString(), base::JSON_PARSE_RFC); - CHECK(snapshot.value); - policy_values.Append(std::move(snapshot.value.value())); - } - std::move(response).Run(std::move(policy_values)); -} - -// Runs on a non-sandbox thread to ensure that response callback is not -// invoked from sandbox process and job tracker thread. -static void RespondWithEmptyList( - base::OnceCallback<void(base::Value)> response) { - base::Value empty(base::Value::Type::LIST); - std::move(response).Run(std::move(empty)); -} - -} // namespace - -ServiceManagerDiagnosticsReceiver::ServiceManagerDiagnosticsReceiver( - scoped_refptr<base::SequencedTaskRunner> origin_task_runner, - base::OnceCallback<void(base::Value)> response) - : response_(std::move(response)), origin_task_runner_(origin_task_runner) {} - -ServiceManagerDiagnosticsReceiver::~ServiceManagerDiagnosticsReceiver() {} - -// This is called by the sandbox's process and job tracking thread and must -// return quickly. -void ServiceManagerDiagnosticsReceiver::ReceiveDiagnostics( - std::unique_ptr<sandbox::PolicyList> policies) { - // Need to run the conversion work on the origin thread. - origin_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&ConvertToValuesAndRespond, std::move(policies), - std::move(response_))); -} - -// This is called by the sandbox's process and job tracking thread and must -// return quickly so we post to the origin thread. -void ServiceManagerDiagnosticsReceiver::OnError(sandbox::ResultCode error) { - origin_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&RespondWithEmptyList, std::move(response_))); -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/win/sandbox_diagnostics.h b/chromium/services/service_manager/sandbox/win/sandbox_diagnostics.h deleted file mode 100644 index 1f89e7ee7a9..00000000000 --- a/chromium/services/service_manager/sandbox/win/sandbox_diagnostics.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANAGER_SANDBOX_WIN_SANDBOX_DIAGNOSTICS_H_ -#define SERVICES_SERVICE_MANAGER_SANDBOX_WIN_SANDBOX_DIAGNOSTICS_H_ - -#include "services/service_manager/sandbox/win/sandbox_win.h" - -#include <stddef.h> - -#include <string> -#include <utility> -#include <vector> - -#include "base/callback.h" -#include "base/sequenced_task_runner.h" -#include "base/values.h" -#include "sandbox/constants.h" -#include "sandbox/win/src/sandbox.h" - -namespace service_manager { - -// Mediates response from sandbox::BrokerServices->GetPolicyDiagnostics. -class ServiceManagerDiagnosticsReceiver - : public sandbox::PolicyDiagnosticsReceiver { - public: - ~ServiceManagerDiagnosticsReceiver() final; - ServiceManagerDiagnosticsReceiver( - scoped_refptr<base::SequencedTaskRunner> origin_task_runner, - base::OnceCallback<void(base::Value)> response); - - // This is called by the sandbox's process and job tracking thread and must - // return quickly. - void ReceiveDiagnostics( - std::unique_ptr<sandbox::PolicyList> policies) override; - - // This is called by the sandbox's process and job tracking thread and must - // return quickly. - void OnError(sandbox::ResultCode error) override; - - private: - base::OnceCallback<void(base::Value)> response_; - scoped_refptr<base::SequencedTaskRunner> origin_task_runner_; -}; -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANAGER_SANDBOX_WIN_SANDBOX_DIAGNOSTICS_H_ diff --git a/chromium/services/service_manager/sandbox/win/sandbox_win.cc b/chromium/services/service_manager/sandbox/win/sandbox_win.cc deleted file mode 100644 index cde75bd0710..00000000000 --- a/chromium/services/service_manager/sandbox/win/sandbox_win.cc +++ /dev/null @@ -1,1138 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "services/service_manager/sandbox/win/sandbox_win.h" - -#include <stddef.h> - -#include <string> -#include <utility> -#include <vector> - -#include "base/command_line.h" -#include "base/debug/activity_tracker.h" -#include "base/feature_list.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/hash/hash.h" -#include "base/hash/sha1.h" -#include "base/logging.h" -#include "base/metrics/field_trial.h" -#include "base/metrics/histogram_functions.h" -#include "base/metrics/histogram_macros.h" -#include "base/no_destructor.h" -#include "base/path_service.h" -#include "base/process/launch.h" -#include "base/stl_util.h" -#include "base/strings/strcat.h" -#include "base/strings/string16.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "base/system/sys_info.h" -#include "base/trace_event/trace_arguments.h" -#include "base/trace_event/trace_event.h" -#include "base/win/iat_patch_function.h" -#include "base/win/scoped_handle.h" -#include "base/win/win_util.h" -#include "base/win/windows_version.h" -#include "sandbox/win/src/app_container_profile.h" -#include "sandbox/win/src/job.h" -#include "sandbox/win/src/process_mitigations.h" -#include "sandbox/win/src/sandbox.h" -#include "sandbox/win/src/sandbox_nt_util.h" -#include "sandbox/win/src/sandbox_policy_base.h" -#include "sandbox/win/src/sandbox_policy_diagnostic.h" -#include "sandbox/win/src/win_utils.h" -#include "services/service_manager/sandbox/features.h" -#include "services/service_manager/sandbox/sandbox_type.h" -#include "services/service_manager/sandbox/switches.h" -#include "services/service_manager/sandbox/win/sandbox_diagnostics.h" - -namespace service_manager { -namespace { - -sandbox::BrokerServices* g_broker_services = NULL; - -HANDLE g_job_object_handle = NULL; - -// The DLLs listed here are known (or under strong suspicion) of causing crashes -// when they are loaded in the renderer. Note: at runtime we generate short -// versions of the dll name only if the dll has an extension. -// For more information about how this list is generated, and how to get off -// of it, see: -// https://sites.google.com/a/chromium.org/dev/Home/third-party-developers -const wchar_t* const kTroublesomeDlls[] = { - L"adialhk.dll", // Kaspersky Internet Security. - L"acpiz.dll", // Unknown. - L"activedetect32.dll", // Lenovo One Key Theater (crbug.com/536056). - L"activedetect64.dll", // Lenovo One Key Theater (crbug.com/536056). - L"airfoilinject3.dll", // Airfoil. - L"akinsofthook32.dll", // Akinsoft Software Engineering. - L"assistant_x64.dll", // Unknown. - L"atcuf64.dll", // Bit Defender Internet Security x64. - L"avcuf64.dll", // Bit Defender Internet Security x64. - L"avgrsstx.dll", // AVG 8. - L"babylonchromepi.dll", // Babylon translator. - L"btkeyind.dll", // Widcomm Bluetooth. - L"cmcsyshk.dll", // CMC Internet Security. - L"cmsetac.dll", // Unknown (suspected malware). - L"cooliris.dll", // CoolIris. - L"cplushook.dll", // Unknown (suspected malware). - L"dockshellhook.dll", // Stardock Objectdock. - L"easyhook32.dll", // GDIPP and others. - L"easyhook64.dll", // Symantec BlueCoat and others. - L"esspd.dll", // Samsung Smart Security ESCORT. - L"googledesktopnetwork3.dll", // Google Desktop Search v5. - L"fwhook.dll", // PC Tools Firewall Plus. - L"guard64.dll", // Comodo Internet Security x64. - L"hookprocesscreation.dll", // Blumentals Program protector. - L"hookterminateapis.dll", // Blumentals and Cyberprinter. - L"hookprintapis.dll", // Cyberprinter. - L"imon.dll", // NOD32 Antivirus. - L"icatcdll.dll", // Samsung Smart Security ESCORT. - L"icdcnl.dll", // Samsung Smart Security ESCORT. - L"ioloHL.dll", // Iolo (System Mechanic). - L"kloehk.dll", // Kaspersky Internet Security. - L"lawenforcer.dll", // Spyware-Browser AntiSpyware (Spybro). - L"libdivx.dll", // DivX. - L"lvprcinj01.dll", // Logitech QuickCam. - L"madchook.dll", // Madshi (generic hooking library). - L"mdnsnsp.dll", // Bonjour. - L"moonsysh.dll", // Moon Secure Antivirus. - L"mpk.dll", // KGB Spy. - L"n64hooks.dll", // Neilsen//NetRatings NetSight. - L"npdivx32.dll", // DivX. - L"npggNT.des", // GameGuard 2008. - L"npggNT.dll", // GameGuard (older). - L"nphooks.dll", // Neilsen//NetRatings NetSight. - L"oawatch.dll", // Online Armor. - L"pastali32.dll", // PastaLeads. - L"pavhook.dll", // Panda Internet Security. - L"pavlsphook.dll", // Panda Antivirus. - L"pavshook.dll", // Panda Antivirus. - L"pavshookwow.dll", // Panda Antivirus. - L"pctavhook.dll", // PC Tools Antivirus. - L"pctgmhk.dll", // PC Tools Spyware Doctor. - L"picrmi32.dll", // PicRec. - L"picrmi64.dll", // PicRec. - L"prntrack.dll", // Pharos Systems. - L"prochook.dll", // Unknown (GBill-Tools?) (crbug.com/974722). - L"protector.dll", // Unknown (suspected malware). - L"radhslib.dll", // Radiant Naomi Internet Filter. - L"radprlib.dll", // Radiant Naomi Internet Filter. - L"rapportnikko.dll", // Trustware Rapport. - L"rlhook.dll", // Trustware Bufferzone. - L"rooksdol.dll", // Trustware Rapport. - L"rndlpepperbrowserrecordhelper.dll", // RealPlayer. - L"rpchromebrowserrecordhelper.dll", // RealPlayer. - L"r3hook.dll", // Kaspersky Internet Security. - L"sahook.dll", // McAfee Site Advisor. - L"sbrige.dll", // Unknown. - L"sc2hook.dll", // Supercopier 2. - L"sdhook32.dll", // Spybot - Search & Destroy Live Protection. - L"sguard.dll", // Iolo (System Guard). - L"smum32.dll", // Spyware Doctor version 6. - L"smumhook.dll", // Spyware Doctor version 5. - L"ssldivx.dll", // DivX. - L"syncor11.dll", // SynthCore Midi interface. - L"systools.dll", // Panda Antivirus. - L"tfwah.dll", // Threatfire (PC tools). - L"wblind.dll", // Stardock Object desktop. - L"wbhelp.dll", // Stardock Object desktop. - L"windowsapihookdll32.dll", // Lenovo One Key Theater (crbug.com/536056). - L"windowsapihookdll64.dll", // Lenovo One Key Theater (crbug.com/536056). - L"winstylerthemehelper.dll" // Tuneup utilities 2006. -}; - -// This is for finch. See also crbug.com/464430 for details. -const base::Feature kEnableCsrssLockdownFeature{ - "EnableCsrssLockdown", base::FEATURE_DISABLED_BY_DEFAULT}; - -// Helps emit trace events for sandbox policy. This mediates memory between -// chrome.exe and chrome.dll. -class PolicyTraceHelper : public base::trace_event::ConvertableToTraceFormat { - public: - PolicyTraceHelper(sandbox::TargetPolicy* policy) { - // |info| must live until JsonString() output is copied. - std::unique_ptr<sandbox::PolicyInfo> info = policy->GetPolicyInfo(); - json_string_ = std::string(info->JsonString()); - } - ~PolicyTraceHelper() override = default; - - // ConvertableToTraceFormat. - void AppendAsTraceFormat(std::string* out) const override { - out->append(json_string_); - } - - private: - std::string json_string_; -}; // PolicyTraceHelper - -#if !defined(NACL_WIN64) -// Adds the policy rules for the path and path\ with the semantic |access|. -// If |children| is set to true, we need to add the wildcard rules to also -// apply the rule to the subfiles and subfolders. -bool AddDirectory(int path, - const wchar_t* sub_dir, - bool children, - sandbox::TargetPolicy::Semantics access, - sandbox::TargetPolicy* policy) { - base::FilePath directory; - if (!base::PathService::Get(path, &directory)) - return false; - - if (sub_dir) - directory = base::MakeAbsoluteFilePath(directory.Append(sub_dir)); - - sandbox::ResultCode result; - result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access, - directory.value().c_str()); - if (result != sandbox::SBOX_ALL_OK) - return false; - - std::wstring directory_str = directory.value() + L"\\"; - if (children) - directory_str += L"*"; - // Otherwise, add the version of the path that ends with a separator. - - result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, access, - directory_str.c_str()); - if (result != sandbox::SBOX_ALL_OK) - return false; - - return true; -} -#endif // !defined(NACL_WIN64) - -// Compares the loaded |module| file name matches |module_name|. -bool IsExpandedModuleName(HMODULE module, const wchar_t* module_name) { - wchar_t path[MAX_PATH]; - DWORD sz = ::GetModuleFileNameW(module, path, base::size(path)); - if ((sz == base::size(path)) || (sz == 0)) { - // XP does not set the last error properly, so we bail out anyway. - return false; - } - if (!::GetLongPathName(path, path, base::size(path))) - return false; - base::FilePath fname(path); - return (fname.BaseName().value() == module_name); -} - -std::vector<std::wstring> GetShortNameVariants(const std::wstring& name) { - std::vector<std::wstring> alt_names; - size_t period = name.rfind(L'.'); - DCHECK_NE(std::string::npos, period); - DCHECK_LE(3U, (name.size() - period)); - if (period <= 8) - return alt_names; - - // The module could have been loaded with a 8.3 short name. We check - // the three most common cases: 'thelongname.dll' becomes - // 'thelon~1.dll', 'thelon~2.dll' and 'thelon~3.dll'. - alt_names.reserve(3); - for (wchar_t ix = '1'; ix <= '3'; ++ix) { - const wchar_t suffix[] = {'~', ix, 0}; - alt_names.push_back( - base::StrCat({name.substr(0, 6), suffix, name.substr(period)})); - } - return alt_names; -} - -// Adds a single dll by |module_name| into the |policy| blocklist. -// If |check_in_browser| is true we only add an unload policy only if the dll -// is also loaded in this process. -void BlocklistAddOneDll(const wchar_t* module_name, - bool check_in_browser, - sandbox::TargetPolicy* policy) { - if (check_in_browser) { - HMODULE module = ::GetModuleHandleW(module_name); - if (module) { - policy->AddDllToUnload(module_name); - DVLOG(1) << "dll to unload found: " << module_name; - } else { - for (const auto& alt_name : GetShortNameVariants(module_name)) { - module = ::GetModuleHandleW(alt_name.c_str()); - // We found it, but because it only has 6 significant letters, we - // want to make sure it is the right one. - if (module && IsExpandedModuleName(module, module_name)) { - // Found a match. We add both forms to the policy. - policy->AddDllToUnload(alt_name.c_str()); - policy->AddDllToUnload(module_name); - return; - } - } - } - } else { - policy->AddDllToUnload(module_name); - for (const auto& alt_name : GetShortNameVariants(module_name)) { - policy->AddDllToUnload(alt_name.c_str()); - } - } -} - -// Adds policy rules for unloaded the known dlls that cause chrome to crash. -// Eviction of injected DLLs is done by the sandbox so that the injected module -// does not get a chance to execute any code. -void AddGenericDllEvictionPolicy(sandbox::TargetPolicy* policy) { - for (int ix = 0; ix != base::size(kTroublesomeDlls); ++ix) - BlocklistAddOneDll(kTroublesomeDlls[ix], true, policy); -} - -// Returns the object path prepended with the current logon session. -base::string16 PrependWindowsSessionPath(const base::char16* object) { - // Cache this because it can't change after process creation. - static DWORD s_session_id = 0; - if (s_session_id == 0) { - HANDLE token; - DWORD session_id_length; - DWORD session_id = 0; - - CHECK(::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)); - CHECK(::GetTokenInformation(token, TokenSessionId, &session_id, - sizeof(session_id), &session_id_length)); - CloseHandle(token); - if (session_id) - s_session_id = session_id; - } - - return base::StringPrintf(L"\\Sessions\\%lu%ls", s_session_id, object); -} - -// Checks if the sandbox can be let to run without a job object assigned. -// Returns true if the job object has to be applied to the sandbox and false -// otherwise. -bool ShouldSetJobLevel(const base::CommandLine& cmd_line) { - // Windows 8 allows nested jobs so we don't need to check if we are in other - // job. - if (base::win::GetVersion() >= base::win::Version::WIN8) - return true; - - BOOL in_job = true; - // Either there is no job yet associated so we must add our job, - if (!::IsProcessInJob(::GetCurrentProcess(), NULL, &in_job)) - NOTREACHED() << "IsProcessInJob failed. " << GetLastError(); - if (!in_job) - return true; - - // ...or there is a job but the JOB_OBJECT_LIMIT_BREAKAWAY_OK limit is set. - JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {}; - if (!::QueryInformationJobObject(NULL, JobObjectExtendedLimitInformation, - &job_info, sizeof(job_info), NULL)) { - NOTREACHED() << "QueryInformationJobObject failed. " << GetLastError(); - return true; - } - if (job_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_BREAKAWAY_OK) - return true; - - // Lastly in place of the flag which was supposed to be used only for running - // Chrome in remote sessions we do this check explicitly here. - // According to MS this flag can be false for a remote session only on Windows - // Server 2012 and newer so if we do the check last we should be on the safe - // side. See: https://msdn.microsoft.com/en-us/library/aa380798.aspx. - if (!::GetSystemMetrics(SM_REMOTESESSION)) { - // TODO(pastarmovj): Even though the number are low, this flag is still - // necessary in some limited set of cases. Remove it once Windows 7 is no - // longer supported together with the rest of the checks in this function. - return !cmd_line.HasSwitch(service_manager::switches::kAllowNoSandboxJob); - } - - // Allow running without the sandbox in this case. This slightly reduces the - // ability of the sandbox to protect its children from spawning new processes - // or preventing them from shutting down Windows or accessing the clipboard. - return false; -} - -// Adds the generic policy rules to a sandbox TargetPolicy. -sandbox::ResultCode AddGenericPolicy(sandbox::TargetPolicy* policy) { - sandbox::ResultCode result; - - // Add the policy for the client side of a pipe. It is just a file - // in the \pipe\ namespace. We restrict it to pipes that start with - // "chrome." so the sandboxed process cannot connect to system services. - result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, - sandbox::TargetPolicy::FILES_ALLOW_ANY, - L"\\??\\pipe\\chrome.*"); - if (result != sandbox::SBOX_ALL_OK) - return result; - - // Allow the server side of sync sockets, which are pipes that have - // the "chrome.sync" namespace and a randomly generated suffix. - result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES, - sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY, - L"\\\\.\\pipe\\chrome.sync.*"); - if (result != sandbox::SBOX_ALL_OK) - return result; - -// Add the policy for debug message only in debug -#ifndef NDEBUG - base::FilePath app_dir; - if (!base::PathService::Get(base::DIR_MODULE, &app_dir)) - return sandbox::SBOX_ERROR_GENERIC; - - wchar_t long_path_buf[MAX_PATH]; - DWORD long_path_return_value = - GetLongPathName(app_dir.value().c_str(), long_path_buf, MAX_PATH); - if (long_path_return_value == 0 || long_path_return_value >= MAX_PATH) - return sandbox::SBOX_ERROR_NO_SPACE; - - base::FilePath debug_message(long_path_buf); - debug_message = debug_message.AppendASCII("debug_message.exe"); - result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_PROCESS, - sandbox::TargetPolicy::PROCESS_MIN_EXEC, - debug_message.value().c_str()); - if (result != sandbox::SBOX_ALL_OK) - return result; -#endif // NDEBUG - -// Add the policy for read-only PDB file access for stack traces. -#if !defined(OFFICIAL_BUILD) - base::FilePath exe; - if (!base::PathService::Get(base::FILE_EXE, &exe)) - return sandbox::SBOX_ERROR_GENERIC; - base::FilePath pdb_path = exe.DirName().Append(L"*.pdb"); - result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, - sandbox::TargetPolicy::FILES_ALLOW_READONLY, - pdb_path.value().c_str()); - if (result != sandbox::SBOX_ALL_OK) - return result; -#endif - -#if defined(SANITIZER_COVERAGE) - DWORD coverage_dir_size = - ::GetEnvironmentVariable(L"SANITIZER_COVERAGE_DIR", NULL, 0); - if (coverage_dir_size == 0) { - LOG(WARNING) << "SANITIZER_COVERAGE_DIR was not set, coverage won't work."; - } else { - std::wstring coverage_dir; - wchar_t* coverage_dir_str = - base::WriteInto(&coverage_dir, coverage_dir_size); - coverage_dir_size = ::GetEnvironmentVariable( - L"SANITIZER_COVERAGE_DIR", coverage_dir_str, coverage_dir_size); - CHECK(coverage_dir.size() == coverage_dir_size); - base::FilePath sancov_path = - base::FilePath(coverage_dir).Append(L"*.sancov"); - result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, - sandbox::TargetPolicy::FILES_ALLOW_ANY, - sancov_path.value().c_str()); - if (result != sandbox::SBOX_ALL_OK) - return result; - } -#endif - - AddGenericDllEvictionPolicy(policy); - return sandbox::SBOX_ALL_OK; -} - -void LogLaunchWarning(sandbox::ResultCode last_warning, DWORD last_error) { - base::UmaHistogramSparse("Process.Sandbox.Launch.WarningResultCode", - last_warning); - base::UmaHistogramSparse("Process.Sandbox.Launch.Warning", last_error); -} - -sandbox::ResultCode AddPolicyForSandboxedProcess( - sandbox::TargetPolicy* policy) { - sandbox::ResultCode result = sandbox::SBOX_ALL_OK; - - // Win8+ adds a device DeviceApi that we don't need. - if (base::win::GetVersion() >= base::win::Version::WIN8) - result = policy->AddKernelObjectToClose(L"File", L"\\Device\\DeviceApi"); - if (result != sandbox::SBOX_ALL_OK) - return result; - - // On 2003/Vista+ the initial token has to be restricted if the main - // token is restricted. - result = policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, - sandbox::USER_LOCKDOWN); - if (result != sandbox::SBOX_ALL_OK) - return result; - // Prevents the renderers from manipulating low-integrity processes. - result = policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED); - if (result != sandbox::SBOX_ALL_OK) - return result; - result = policy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); - if (result != sandbox::SBOX_ALL_OK) - return result; - policy->SetLockdownDefaultDacl(); - - result = policy->SetAlternateDesktop(true); - if (result != sandbox::SBOX_ALL_OK) { - // We ignore the result of setting the alternate desktop, however log - // a launch warning. - LogLaunchWarning(result, ::GetLastError()); - DLOG(WARNING) << "Failed to apply desktop security to the renderer"; - result = sandbox::SBOX_ALL_OK; - } - - return result; -} - -// This code is test only, and attempts to catch unsafe uses of -// DuplicateHandle() that copy privileged handles into sandboxed processes. -#if !defined(OFFICIAL_BUILD) && !defined(COMPONENT_BUILD) -base::win::IATPatchFunction& GetIATPatchFunctionHandle() { - static base::NoDestructor<base::win::IATPatchFunction> - iat_patch_duplicate_handle; - return *iat_patch_duplicate_handle; -} - -typedef BOOL(WINAPI* DuplicateHandleFunctionPtr)(HANDLE source_process_handle, - HANDLE source_handle, - HANDLE target_process_handle, - LPHANDLE target_handle, - DWORD desired_access, - BOOL inherit_handle, - DWORD options); - -DuplicateHandleFunctionPtr g_iat_orig_duplicate_handle; - -NtQueryObject g_QueryObject = NULL; - -static const char* kDuplicateHandleWarning = - "You are attempting to duplicate a privileged handle into a sandboxed" - " process.\n Please contact security@chromium.org for assistance."; - -void CheckDuplicateHandle(HANDLE handle) { - // Get the object type (32 characters is safe; current max is 14). - BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)]; - OBJECT_TYPE_INFORMATION* type_info = - reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer); - ULONG size = sizeof(buffer) - sizeof(wchar_t); - NTSTATUS error; - error = g_QueryObject(handle, ObjectTypeInformation, type_info, size, &size); - CHECK(NT_SUCCESS(error)); - type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0'; - - // Get the object basic information. - OBJECT_BASIC_INFORMATION basic_info; - size = sizeof(basic_info); - error = - g_QueryObject(handle, ObjectBasicInformation, &basic_info, size, &size); - CHECK(NT_SUCCESS(error)); - - CHECK(!(basic_info.GrantedAccess & WRITE_DAC)) << kDuplicateHandleWarning; - - if (0 == _wcsicmp(type_info->Name.Buffer, L"Process")) { - const ACCESS_MASK kDangerousMask = - ~static_cast<DWORD>(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE); - CHECK(!(basic_info.GrantedAccess & kDangerousMask)) - << kDuplicateHandleWarning; - } -} - -BOOL WINAPI DuplicateHandlePatch(HANDLE source_process_handle, - HANDLE source_handle, - HANDLE target_process_handle, - LPHANDLE target_handle, - DWORD desired_access, - BOOL inherit_handle, - DWORD options) { - // Duplicate the handle so we get the final access mask. - if (!g_iat_orig_duplicate_handle(source_process_handle, source_handle, - target_process_handle, target_handle, - desired_access, inherit_handle, options)) - return FALSE; - - // We're not worried about broker handles or not crossing process boundaries. - if (source_process_handle == target_process_handle || - target_process_handle == ::GetCurrentProcess()) - return TRUE; - - // Only sandboxed children are placed in jobs, so just check them. - BOOL is_in_job = FALSE; - if (!::IsProcessInJob(target_process_handle, NULL, &is_in_job)) { - // We need a handle with permission to check the job object. - if (ERROR_ACCESS_DENIED == ::GetLastError()) { - HANDLE temp_handle; - CHECK(g_iat_orig_duplicate_handle( - ::GetCurrentProcess(), target_process_handle, ::GetCurrentProcess(), - &temp_handle, PROCESS_QUERY_INFORMATION, FALSE, 0)); - base::win::ScopedHandle process(temp_handle); - CHECK(::IsProcessInJob(process.Get(), NULL, &is_in_job)); - } - } - - if (is_in_job) { - // We never allow inheritable child handles. - CHECK(!inherit_handle) << kDuplicateHandleWarning; - - // Duplicate the handle again, to get the final permissions. - HANDLE temp_handle; - CHECK(g_iat_orig_duplicate_handle(target_process_handle, *target_handle, - ::GetCurrentProcess(), &temp_handle, 0, - FALSE, DUPLICATE_SAME_ACCESS)); - base::win::ScopedHandle handle(temp_handle); - - // Callers use CHECK macro to make sure we get the right stack. - CheckDuplicateHandle(handle.Get()); - } - - return TRUE; -} -#endif - -bool IsAppContainerEnabled() { - if (base::win::GetVersion() < base::win::Version::WIN8) - return false; - - return base::FeatureList::IsEnabled( - {"RendererAppContainer", base::FEATURE_DISABLED_BY_DEFAULT}); -} - -sandbox::ResultCode SetJobMemoryLimit(const base::CommandLine& cmd_line, - sandbox::TargetPolicy* policy) { - DCHECK_NE(policy->GetJobLevel(), sandbox::JOB_NONE); - -#ifdef _WIN64 - size_t memory_limit = static_cast<size_t>(sandbox::kDataSizeLimit); - - // Note that this command line flag hasn't been fetched by all - // callers of SetJobLevel, only those in this file. - SandboxType sandbox_type = - service_manager::SandboxTypeFromCommandLine(cmd_line); - if (sandbox_type == SandboxType::kGpu || - sandbox_type == SandboxType::kRenderer) { - int64_t GB = 1024 * 1024 * 1024; - // Allow the GPU/RENDERER process's sandbox to access more physical memory - // if it's available on the system. - int64_t physical_memory = base::SysInfo::AmountOfPhysicalMemory(); - if (physical_memory > 16 * GB) { - memory_limit = 16 * GB; - } else if (physical_memory > 8 * GB) { - memory_limit = 8 * GB; - } - } - return policy->SetJobMemoryLimit(memory_limit); -#else - return sandbox::SBOX_ALL_OK; -#endif -} - -// Generate a unique sandbox AC profile for the appcontainer based on the SHA1 -// hash of the appcontainer_id. This does not need to be secure so using SHA1 -// isn't a security concern. -base::string16 GetAppContainerProfileName( - const std::string& appcontainer_id, - service_manager::SandboxType sandbox_type) { - DCHECK(sandbox_type == SandboxType::kGpu || - sandbox_type == SandboxType::kXrCompositing); - auto sha1 = base::SHA1HashString(appcontainer_id); - std::string sandbox_base_name = (sandbox_type == SandboxType::kXrCompositing) - ? std::string("cr.sb.xr") - : std::string("cr.sb.gpu"); - std::string profile_name = base::StrCat( - {sandbox_base_name, base::HexEncode(sha1.data(), sha1.size())}); - // CreateAppContainerProfile requires that the profile name is at most 64 - // characters but 50 on WCOS systems. The size of sha1 is a constant 40, - // so validate that the base names are sufficiently short that the total - // length is valid on all systems. - DCHECK_LE(profile_name.length(), 50U); - return base::UTF8ToWide(profile_name); -} - -sandbox::ResultCode SetupAppContainerProfile( - sandbox::AppContainerProfile* profile, - const base::CommandLine& command_line, - service_manager::SandboxType sandbox_type) { - if (sandbox_type != SandboxType::kGpu && - sandbox_type != SandboxType::kXrCompositing) - return sandbox::SBOX_ERROR_UNSUPPORTED; - - if (sandbox_type == SandboxType::kGpu && - !profile->AddImpersonationCapability(L"chromeInstallFiles")) { - DLOG(ERROR) << "AppContainerProfile::AddImpersonationCapability(" - "chromeInstallFiles) failed"; - return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY; - } - - if ((sandbox_type == SandboxType::kXrCompositing || - sandbox_type == SandboxType::kGpu) && - !profile->AddCapability(L"lpacPnpNotifications")) { - DLOG(ERROR) - << "AppContainerProfile::AddCapability(lpacPnpNotifications) failed"; - return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY; - } - - if (sandbox_type == SandboxType::kXrCompositing && - !profile->AddCapability(L"chromeInstallFiles")) { - DLOG(ERROR) - << "AppContainerProfile::AddCapability(chromeInstallFiles) failed"; - return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY; - } - - std::vector<base::string16> base_caps = { - L"lpacChromeInstallFiles", L"registryRead", - }; - - if (sandbox_type == SandboxType::kGpu) { - auto cmdline_caps = base::SplitString( - command_line.GetSwitchValueNative( - service_manager::switches::kAddGpuAppContainerCaps), - L",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - base_caps.insert(base_caps.end(), cmdline_caps.begin(), cmdline_caps.end()); - } - - if (sandbox_type == SandboxType::kXrCompositing) { - auto cmdline_caps = base::SplitString( - command_line.GetSwitchValueNative( - service_manager::switches::kAddXrAppContainerCaps), - L",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - base_caps.insert(base_caps.end(), cmdline_caps.begin(), cmdline_caps.end()); - } - - for (const auto& cap : base_caps) { - if (!profile->AddCapability(cap.c_str())) { - DLOG(ERROR) << "AppContainerProfile::AddCapability() failed"; - return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY; - } - } - - // Enable LPAC for GPU process, but not for XRCompositor service. - if (sandbox_type == SandboxType::kGpu && - base::FeatureList::IsEnabled(service_manager::features::kGpuLPAC)) { - profile->SetEnableLowPrivilegeAppContainer(true); - } - - return sandbox::SBOX_ALL_OK; -} - -} // namespace - -// static -sandbox::ResultCode SandboxWin::SetJobLevel(const base::CommandLine& cmd_line, - sandbox::JobLevel job_level, - uint32_t ui_exceptions, - sandbox::TargetPolicy* policy) { - if (!ShouldSetJobLevel(cmd_line)) - return policy->SetJobLevel(sandbox::JOB_NONE, 0); - - sandbox::ResultCode ret = policy->SetJobLevel(job_level, ui_exceptions); - if (ret != sandbox::SBOX_ALL_OK) - return ret; - - return SetJobMemoryLimit(cmd_line, policy); -} - -// TODO(jschuh): Need get these restrictions applied to NaCl and Pepper. -// Just have to figure out what needs to be warmed up first. -// static -sandbox::ResultCode SandboxWin::AddBaseHandleClosePolicy( - sandbox::TargetPolicy* policy) { - if (base::FeatureList::IsEnabled(kEnableCsrssLockdownFeature)) { - // Close all ALPC ports. - sandbox::ResultCode ret = policy->SetDisconnectCsrss(); - if (ret != sandbox::SBOX_ALL_OK) { - return ret; - } - } - - // TODO(cpu): Add back the BaseNamedObjects policy. - base::string16 object_path = PrependWindowsSessionPath( - L"\\BaseNamedObjects\\windows_shell_global_counters"); - return policy->AddKernelObjectToClose(L"Section", object_path.data()); -} - -// static -sandbox::ResultCode SandboxWin::AddAppContainerPolicy( - sandbox::TargetPolicy* policy, - const wchar_t* sid) { - if (IsAppContainerEnabled()) - return policy->SetLowBox(sid); - return sandbox::SBOX_ALL_OK; -} - -// static -sandbox::ResultCode SandboxWin::AddWin32kLockdownPolicy( - sandbox::TargetPolicy* policy, - bool enable_opm) { -#if !defined(NACL_WIN64) - if (!service_manager::IsWin32kLockdownEnabled()) - return sandbox::SBOX_ALL_OK; - - sandbox::MitigationFlags flags = policy->GetProcessMitigations(); - // Check not enabling twice. Should not happen. - DCHECK_EQ(0U, flags & sandbox::MITIGATION_WIN32K_DISABLE); - - flags |= sandbox::MITIGATION_WIN32K_DISABLE; - sandbox::ResultCode result = policy->SetProcessMitigations(flags); - if (result != sandbox::SBOX_ALL_OK) - return result; - - result = - policy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN, - enable_opm ? sandbox::TargetPolicy::IMPLEMENT_OPM_APIS - : sandbox::TargetPolicy::FAKE_USER_GDI_INIT, - nullptr); - if (result != sandbox::SBOX_ALL_OK) - return result; - if (enable_opm) - policy->SetEnableOPMRedirection(); - - return result; -#else - return sandbox::SBOX_ALL_OK; -#endif -} - -// static -sandbox::ResultCode SandboxWin::AddAppContainerProfileToPolicy( - const base::CommandLine& command_line, - service_manager::SandboxType sandbox_type, - const std::string& appcontainer_id, - sandbox::TargetPolicy* policy) { - if (base::win::GetVersion() < base::win::Version::WIN10_RS1) - return sandbox::SBOX_ALL_OK; - base::string16 profile_name = - GetAppContainerProfileName(appcontainer_id, sandbox_type); - sandbox::ResultCode result = - policy->AddAppContainerProfile(profile_name.c_str(), true); - if (result != sandbox::SBOX_ALL_OK) - return result; - - scoped_refptr<sandbox::AppContainerProfile> profile = - policy->GetAppContainerProfile(); - result = SetupAppContainerProfile(profile.get(), command_line, sandbox_type); - if (result != sandbox::SBOX_ALL_OK) - return result; - - DWORD granted_access; - BOOL granted_access_status; - bool access_check = - profile->AccessCheck(command_line.GetProgram().value().c_str(), - SE_FILE_OBJECT, GENERIC_READ | GENERIC_EXECUTE, - &granted_access, &granted_access_status) && - granted_access_status; - if (!access_check) - return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_ACCESS_CHECK; - - return sandbox::SBOX_ALL_OK; -} - -// static -bool SandboxWin::IsAppContainerEnabledForSandbox( - const base::CommandLine& command_line, - SandboxType sandbox_type) { - if (sandbox_type != SandboxType::kGpu) - return false; - if (base::win::GetVersion() < base::win::Version::WIN10_RS1) - return false; - return base::FeatureList::IsEnabled( - service_manager::features::kGpuAppContainer); -} - -// static -bool SandboxWin::InitBrokerServices(sandbox::BrokerServices* broker_services) { - // TODO(abarth): DCHECK(CalledOnValidThread()); - // See <http://b/1287166>. - DCHECK(broker_services); - DCHECK(!g_broker_services); - sandbox::ResultCode result = broker_services->Init(); - g_broker_services = broker_services; - -// In non-official builds warn about dangerous uses of DuplicateHandle. This -// isn't useful under a component build, since there will be multiple modules, -// each of which may have a slot to patch (if the symbol is even present). -#if !defined(OFFICIAL_BUILD) && !defined(COMPONENT_BUILD) - BOOL is_in_job = FALSE; - CHECK(::IsProcessInJob(::GetCurrentProcess(), NULL, &is_in_job)); - if (!is_in_job && !GetIATPatchFunctionHandle().is_patched()) { - HMODULE module = NULL; - wchar_t module_name[MAX_PATH]; - CHECK(::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - reinterpret_cast<LPCWSTR>(InitBrokerServices), - &module)); - DWORD result = ::GetModuleFileNameW(module, module_name, MAX_PATH); - if (result && (result != MAX_PATH)) { - ResolveNTFunctionPtr("NtQueryObject", &g_QueryObject); - result = GetIATPatchFunctionHandle().Patch( - module_name, "kernel32.dll", "DuplicateHandle", - reinterpret_cast<void*>(DuplicateHandlePatch)); - CHECK_EQ(0u, result); - g_iat_orig_duplicate_handle = - reinterpret_cast<DuplicateHandleFunctionPtr>( - GetIATPatchFunctionHandle().original_function()); - } - } -#endif - - return sandbox::SBOX_ALL_OK == result; -} - -// static -bool SandboxWin::InitTargetServices(sandbox::TargetServices* target_services) { - DCHECK(target_services); - sandbox::ResultCode result = target_services->Init(); - return sandbox::SBOX_ALL_OK == result; -} - -// static -sandbox::ResultCode SandboxWin::StartSandboxedProcess( - base::CommandLine* cmd_line, - const std::string& process_type, - const base::HandlesToInheritVector& handles_to_inherit, - service_manager::SandboxDelegate* delegate, - base::Process* process) { - const base::CommandLine& launcher_process_command_line = - *base::CommandLine::ForCurrentProcess(); - - // Propagate the --allow-no-job flag if present. - if (launcher_process_command_line.HasSwitch( - service_manager::switches::kAllowNoSandboxJob) && - !cmd_line->HasSwitch(service_manager::switches::kAllowNoSandboxJob)) { - cmd_line->AppendSwitch(service_manager::switches::kAllowNoSandboxJob); - } - - service_manager::SandboxType sandbox_type = delegate->GetSandboxType(); - if (service_manager::IsUnsandboxedSandboxType(sandbox_type) || - cmd_line->HasSwitch(service_manager::switches::kNoSandbox) || - launcher_process_command_line.HasSwitch( - service_manager::switches::kNoSandbox)) { - base::LaunchOptions options; - options.handles_to_inherit = handles_to_inherit; - BOOL in_job = true; - // Prior to Windows 8 nested jobs aren't possible. - if (sandbox_type == SandboxType::kNetwork && - (base::win::GetVersion() >= base::win::Version::WIN8 || - (::IsProcessInJob(::GetCurrentProcess(), nullptr, &in_job) && - !in_job))) { - // Launch the process in a job to ensure that the network process doesn't - // outlive the browser. This could happen if there is a lot of I/O on - // process shutdown, in which case TerminateProcess would fail. - // https://crbug.com/820996 - if (!g_job_object_handle) { - sandbox::Job job_obj; - DWORD result = job_obj.Init(sandbox::JOB_UNPROTECTED, nullptr, 0, 0); - if (result != ERROR_SUCCESS) - return sandbox::SBOX_ERROR_CANNOT_INIT_JOB; - g_job_object_handle = job_obj.Take().Take(); - } - options.job_handle = g_job_object_handle; - } - *process = base::LaunchProcess(*cmd_line, options); - return sandbox::SBOX_ALL_OK; - } - - scoped_refptr<sandbox::TargetPolicy> policy = - g_broker_services->CreatePolicy(); - - // Add any handles to be inherited to the policy. - for (HANDLE handle : handles_to_inherit) - policy->AddHandleToShare(handle); - - // Pre-startup mitigations. - sandbox::MitigationFlags mitigations = - sandbox::MITIGATION_HEAP_TERMINATE | - sandbox::MITIGATION_BOTTOM_UP_ASLR | - sandbox::MITIGATION_DEP | - sandbox::MITIGATION_DEP_NO_ATL_THUNK | - sandbox::MITIGATION_EXTENSION_POINT_DISABLE | - sandbox::MITIGATION_SEHOP | - sandbox::MITIGATION_NONSYSTEM_FONT_DISABLE | - sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE | - sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL | - sandbox::MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION; - - sandbox::ResultCode result = policy->SetProcessMitigations(mitigations); - if (result != sandbox::SBOX_ALL_OK) - return result; - -#if !defined(NACL_WIN64) - if (process_type == service_manager::switches::kRendererProcess && - service_manager::IsWin32kLockdownEnabled()) { - result = SandboxWin::AddWin32kLockdownPolicy(policy.get(), false); - if (result != sandbox::SBOX_ALL_OK) - return result; - } -#endif - - // Post-startup mitigations. - mitigations = sandbox::MITIGATION_DLL_SEARCH_ORDER; - if (!cmd_line->HasSwitch(switches::kAllowThirdPartyModules)) - mitigations |= sandbox::MITIGATION_FORCE_MS_SIGNED_BINS; - if (sandbox_type == SandboxType::kNetwork || - sandbox_type == SandboxType::kAudio || - sandbox_type == SandboxType::kIconReader) { - mitigations |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE; - } - // TODO(wfh): Relax strict handle checks for network process until root cause - // for this crash can be resolved. See https://crbug.com/939590. - if (sandbox_type != SandboxType::kNetwork) - mitigations |= sandbox::MITIGATION_STRICT_HANDLE_CHECKS; - - result = policy->SetDelayedProcessMitigations(mitigations); - if (result != sandbox::SBOX_ALL_OK) - return result; - - result = SetJobLevel(*cmd_line, sandbox::JOB_LOCKDOWN, 0, policy.get()); - if (result != sandbox::SBOX_ALL_OK) - return result; - - if (!delegate->DisableDefaultPolicy()) { - result = AddPolicyForSandboxedProcess(policy.get()); - if (result != sandbox::SBOX_ALL_OK) - return result; - } - - if (process_type == service_manager::switches::kGpuProcess && - base::FeatureList::IsEnabled( - {"GpuLockdownDefaultDacl", base::FEATURE_ENABLED_BY_DEFAULT})) { - policy->SetLockdownDefaultDacl(); - policy->AddRestrictingRandomSid(); - } - -#if !defined(NACL_WIN64) - if (process_type == service_manager::switches::kRendererProcess || - process_type == service_manager::switches::kPpapiPluginProcess || - sandbox_type == SandboxType::kPrintCompositor) { - AddDirectory(base::DIR_WINDOWS_FONTS, NULL, true, - sandbox::TargetPolicy::FILES_ALLOW_READONLY, policy.get()); - } -#endif - - result = AddGenericPolicy(policy.get()); - if (result != sandbox::SBOX_ALL_OK) { - NOTREACHED(); - return result; - } - - std::string appcontainer_id; - if (IsAppContainerEnabledForSandbox(*cmd_line, sandbox_type) && - delegate->GetAppContainerId(&appcontainer_id)) { - result = AddAppContainerProfileToPolicy(*cmd_line, sandbox_type, - appcontainer_id, policy.get()); - DCHECK(result == sandbox::SBOX_ALL_OK); - if (result != sandbox::SBOX_ALL_OK) - return result; - } - - // Allow the renderer, gpu and utility processes to access the log file. - if (process_type == service_manager::switches::kRendererProcess || - process_type == service_manager::switches::kGpuProcess || - process_type == service_manager::switches::kUtilityProcess) { - if (logging::IsLoggingToFileEnabled()) { - DCHECK(base::FilePath(logging::GetLogFileFullPath()).IsAbsolute()); - result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, - sandbox::TargetPolicy::FILES_ALLOW_ANY, - logging::GetLogFileFullPath().c_str()); - if (result != sandbox::SBOX_ALL_OK) - return result; - } - } - -#if !defined(OFFICIAL_BUILD) - // If stdout/stderr point to a Windows console, these calls will - // have no effect. These calls can fail with SBOX_ERROR_BAD_PARAMS. - policy->SetStdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE)); - policy->SetStderrHandle(GetStdHandle(STD_ERROR_HANDLE)); -#endif - - if (!delegate->PreSpawnTarget(policy.get())) - return sandbox::SBOX_ERROR_DELEGATE_PRE_SPAWN; - - TRACE_EVENT_BEGIN0("startup", "StartProcessWithAccess::LAUNCHPROCESS"); - - PROCESS_INFORMATION temp_process_info = {}; - sandbox::ResultCode last_warning = sandbox::SBOX_ALL_OK; - DWORD last_error = ERROR_SUCCESS; - result = g_broker_services->SpawnTarget( - cmd_line->GetProgram().value().c_str(), - cmd_line->GetCommandLineString().c_str(), policy, &last_warning, - &last_error, &temp_process_info); - - base::win::ScopedProcessInformation target(temp_process_info); - - TRACE_EVENT_END0("startup", "StartProcessWithAccess::LAUNCHPROCESS"); - - // Trace policy as processes are started. Useful for both failure and success. - TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("sandbox"), "processLaunch", - TRACE_EVENT_SCOPE_PROCESS, "sandboxType", - GetSandboxTypeInEnglish(delegate->GetSandboxType()), - "policy", - std::make_unique<PolicyTraceHelper>(policy.get())); - - if (sandbox::SBOX_ALL_OK != result) { - base::UmaHistogramSparse("Process.Sandbox.Launch.Error", last_error); - if (result == sandbox::SBOX_ERROR_GENERIC) - DPLOG(ERROR) << "Failed to launch process"; - else - DLOG(ERROR) << "Failed to launch process. Error: " << result; - return result; - } - - base::debug::GlobalActivityTracker* tracker = - base::debug::GlobalActivityTracker::Get(); - if (tracker) { - tracker->RecordProcessLaunch(target.process_id(), - cmd_line->GetCommandLineString()); - } - - if (sandbox::SBOX_ALL_OK != last_warning) - LogLaunchWarning(last_warning, last_error); - - delegate->PostSpawnTarget(target.process_handle()); - CHECK(ResumeThread(target.thread_handle()) != static_cast<DWORD>(-1)); - - *process = base::Process(target.TakeProcessHandle()); - return sandbox::SBOX_ALL_OK; -} - -// static -sandbox::ResultCode SandboxWin::GetPolicyDiagnostics( - base::OnceCallback<void(base::Value)> response) { - CHECK(g_broker_services); - CHECK(!response.is_null()); - auto receiver = std::make_unique<ServiceManagerDiagnosticsReceiver>( - base::SequencedTaskRunnerHandle::Get(), std::move(response)); - return g_broker_services->GetPolicyDiagnostics(std::move(receiver)); -} - -void BlocklistAddOneDllForTesting(const wchar_t* module_name, - bool check_in_browser, - sandbox::TargetPolicy* policy) { - BlocklistAddOneDll(module_name, check_in_browser, policy); -} - -// static -std::string SandboxWin::GetSandboxTypeInEnglish(SandboxType sandbox_type) { - switch (sandbox_type) { - case SandboxType::kNoSandbox: - return "Unsandboxed"; - case SandboxType::kNoSandboxAndElevatedPrivileges: - return "Unsandboxed (Elevated)"; - case SandboxType::kXrCompositing: - return "XR Compositing"; - case SandboxType::kRenderer: - return "Renderer"; - case SandboxType::kUtility: - return "Utility"; - case SandboxType::kGpu: - return "GPU"; - case SandboxType::kPpapi: - return "PPAPI"; - case SandboxType::kNetwork: - return "Network"; - case SandboxType::kCdm: - return "CDM"; - case SandboxType::kPrintCompositor: - return "Print Compositor"; - case SandboxType::kAudio: - return "Audio"; - case SandboxType::kSpeechRecognition: - return "Speech Recognition"; - case SandboxType::kProxyResolver: - return "Proxy Resolver"; - case SandboxType::kPdfConversion: - return "PDF Conversion"; - case SandboxType::kSharingService: - return "Sharing"; - case SandboxType::kVideoCapture: - return "Video Capture"; - case SandboxType::kIconReader: - return "Icon Reader"; - } -} - -} // namespace service_manager diff --git a/chromium/services/service_manager/sandbox/win/sandbox_win.h b/chromium/services/service_manager/sandbox/win/sandbox_win.h deleted file mode 100644 index 6614a82461e..00000000000 --- a/chromium/services/service_manager/sandbox/win/sandbox_win.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SERVICES_SERVICE_MANANGER_SANDBOX_WIN_SANDBOX_WIN_H_ -#define SERVICES_SERVICE_MANANGER_SANDBOX_WIN_SANDBOX_WIN_H_ - -#include <stdint.h> - -#include <string> - -#include "base/bind.h" -#include "base/callback_forward.h" -#include "base/process/launch.h" -#include "base/process/process_handle.h" -#include "sandbox/win/src/sandbox_types.h" -#include "sandbox/win/src/security_level.h" -#include "services/service_manager/sandbox/export.h" -#include "services/service_manager/sandbox/sandbox_delegate.h" -#include "services/service_manager/sandbox/sandbox_type.h" - -namespace base { -class CommandLine; -class Value; -} // namespace base - -namespace sandbox { -class BrokerServices; -class TargetPolicy; -class TargetServices; -} // namespace sandbox - -namespace service_manager { - -class SERVICE_MANAGER_SANDBOX_EXPORT SandboxWin { - public: - static sandbox::ResultCode StartSandboxedProcess( - base::CommandLine* cmd_line, - const std::string& process_type, - const base::HandlesToInheritVector& handles_to_inherit, - SandboxDelegate* delegate, - base::Process* process); - - // Wrapper around sandbox::TargetPolicy::SetJobLevel that checks if the - // sandbox should be let to run without a job object assigned. - static sandbox::ResultCode SetJobLevel(const base::CommandLine& cmd_line, - sandbox::JobLevel job_level, - uint32_t ui_exceptions, - sandbox::TargetPolicy* policy); - - // Closes handles that are opened at process creation and initialization. - static sandbox::ResultCode AddBaseHandleClosePolicy( - sandbox::TargetPolicy* policy); - - // Add AppContainer policy for |sid| on supported OS. - static sandbox::ResultCode AddAppContainerPolicy( - sandbox::TargetPolicy* policy, - const wchar_t* sid); - - // Add the win32k lockdown policy on supported OS. - static sandbox::ResultCode AddWin32kLockdownPolicy( - sandbox::TargetPolicy* policy, - bool enable_opm); - - // Add the AppContainer sandbox profile to the policy. |sandbox_type| - // determines what policy is enabled. |appcontainer_id| is used to create - // a unique package SID, it can be anything the caller wants. - static sandbox::ResultCode AddAppContainerProfileToPolicy( - const base::CommandLine& command_line, - service_manager::SandboxType sandbox_type, - const std::string& appcontainer_id, - sandbox::TargetPolicy* policy); - - // Returns whether the AppContainer sandbox is enabled or not for a specific - // sandbox type from |command_line| and |sandbox_type|. - static bool IsAppContainerEnabledForSandbox( - const base::CommandLine& command_line, - service_manager::SandboxType sandbox_type); - - static bool InitBrokerServices(sandbox::BrokerServices* broker_services); - static bool InitTargetServices(sandbox::TargetServices* target_services); - - // Report diagnostic information about policies applied to sandboxed - // processes. This is a snapshot and may describe processes which - // have subsequently finished. This can be invoked on any sequence and posts - // to |response| to the origin sequence on completion. |response| - // will be an empty value if an error is encountered. - static sandbox::ResultCode GetPolicyDiagnostics( - base::OnceCallback<void(base::Value)> response); - - // Provides a friendly name for the sandbox for chrome://sandbox and tracing. - static std::string GetSandboxTypeInEnglish(SandboxType sandbox_type); -}; - -SERVICE_MANAGER_SANDBOX_EXPORT -void BlocklistAddOneDllForTesting(const wchar_t* module_name, - bool check_in_browser, - sandbox::TargetPolicy* policy); - -} // namespace service_manager - -#endif // SERVICES_SERVICE_MANANGER_SANDBOX_WIN_SANDBOX_WIN_H_ diff --git a/chromium/services/service_manager/service_instance.cc b/chromium/services/service_manager/service_instance.cc index 2d5bb77181d..658754e4d0d 100644 --- a/chromium/services/service_manager/service_instance.cc +++ b/chromium/services/service_manager/service_instance.cc @@ -156,7 +156,7 @@ void ServiceInstance::StartWithRemote( #if !defined(OS_IOS) bool ServiceInstance::StartWithProcessHost( std::unique_ptr<ServiceProcessHost> host, - SandboxType sandbox_type) { + sandbox::policy::SandboxType sandbox_type) { DCHECK(!service_remote_); DCHECK(!process_host_); @@ -473,6 +473,7 @@ void ServiceInstance::RegisterServiceInstance( if (!service_manager_->RegisterService(identity, std::move(service_remote), std::move(metadata_receiver))) { std::move(callback).Run(mojom::ConnectResult::ACCESS_DENIED); + return; } std::move(callback).Run(mojom::ConnectResult::SUCCEEDED); diff --git a/chromium/services/service_manager/service_instance.h b/chromium/services/service_manager/service_instance.h index 20a3700f130..5600d48c5de 100644 --- a/chromium/services/service_manager/service_instance.h +++ b/chromium/services/service_manager/service_instance.h @@ -24,13 +24,13 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/remote.h" +#include "sandbox/policy/sandbox_type.h" #include "services/service_manager/public/cpp/identity.h" #include "services/service_manager/public/cpp/manifest.h" #include "services/service_manager/public/mojom/connector.mojom.h" #include "services/service_manager/public/mojom/service.mojom.h" #include "services/service_manager/public/mojom/service_control.mojom.h" #include "services/service_manager/public/mojom/service_manager.mojom.h" -#include "services/service_manager/sandbox/sandbox_type.h" namespace service_manager { @@ -65,7 +65,7 @@ class ServiceInstance : public mojom::Connector, #if !defined(OS_IOS) // Starts this instance from a path to a service executable on disk. bool StartWithProcessHost(std::unique_ptr<ServiceProcessHost> host, - SandboxType sandbox_type); + sandbox::policy::SandboxType sandbox_type); #endif // !defined(OS_IOS) // Binds an endpoint for this instance to receive metadata about its diff --git a/chromium/services/service_manager/service_manager.cc b/chromium/services/service_manager/service_manager.cc index ceb1ea74f24..df2a4200202 100644 --- a/chromium/services/service_manager/service_manager.cc +++ b/chromium/services/service_manager/service_manager.cc @@ -20,6 +20,7 @@ #include "base/token.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" +#include "sandbox/policy/sandbox_type.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/constants.h" #include "services/service_manager/public/cpp/manifest_builder.h" @@ -28,7 +29,6 @@ #include "services/service_manager/public/mojom/service.mojom.h" #include "services/service_manager/public/mojom/service_control.mojom.h" #include "services/service_manager/public/mojom/service_manager.mojom.h" -#include "services/service_manager/sandbox/sandbox_type.h" #include "services/service_manager/service_instance.h" #include "services/service_manager/service_process_host.h" @@ -77,16 +77,17 @@ class DefaultServiceProcessHost : public ServiceProcessHost { ~DefaultServiceProcessHost() override = default; - mojo::PendingRemote<mojom::Service> Launch(const Identity& identity, - SandboxType sandbox_type, - const base::string16& display_name, - LaunchCallback callback) override { + mojo::PendingRemote<mojom::Service> Launch( + const Identity& identity, + sandbox::policy::SandboxType sandbox_type, + const base::string16& display_name, + LaunchCallback callback) override { #if defined(OS_IOS) return mojo::NullRemote(); #else // TODO(https://crbug.com/781334): Support sandboxing. - CHECK_EQ(sandbox_type, SandboxType::kNoSandbox); - return launcher_.Start(identity, SandboxType::kNoSandbox, + CHECK_EQ(sandbox_type, sandbox::policy::SandboxType::kNoSandbox); + return launcher_.Start(identity, sandbox::policy::SandboxType::kNoSandbox, std::move(callback)); #endif // defined(OS_IOS) } @@ -160,7 +161,7 @@ ServiceManager::ServiceManager(const std::vector<Manifest>& manifests, service_manager_instance_->SetPID(GetCurrentPid()); mojo::PendingRemote<mojom::Service> remote; - service_binding_.Bind(remote.InitWithNewPipeAndPassReceiver()); + service_receiver_.Bind(remote.InitWithNewPipeAndPassReceiver()); service_manager_instance_->StartWithRemote(std::move(remote)); } @@ -308,10 +309,10 @@ ServiceInstance* ServiceManager::FindOrCreateMatchingTargetInstance( case Manifest::ExecutionMode::kOutOfProcessBuiltin: { auto process_host = delegate_->CreateProcessHostForBuiltinServiceInstance( target_instance->identity()); - if (!process_host || - !target_instance->StartWithProcessHost( - std::move(process_host), - UtilitySandboxTypeFromString(manifest->options.sandbox_type))) { + if (!process_host || !target_instance->StartWithProcessHost( + std::move(process_host), + sandbox::policy::UtilitySandboxTypeFromString( + manifest->options.sandbox_type))) { DestroyInstance(target_instance); return nullptr; } @@ -324,10 +325,10 @@ ServiceInstance* ServiceManager::FindOrCreateMatchingTargetInstance( auto process_host = delegate_->CreateProcessHostForServiceExecutable( service_exe_root.AppendASCII(manifest->service_name + kServiceExecutableExtension)); - if (!process_host || - !target_instance->StartWithProcessHost( - std::move(process_host), - UtilitySandboxTypeFromString(manifest->options.sandbox_type))) { + if (!process_host || !target_instance->StartWithProcessHost( + std::move(process_host), + sandbox::policy::UtilitySandboxTypeFromString( + manifest->options.sandbox_type))) { DestroyInstance(target_instance); return nullptr; } diff --git a/chromium/services/service_manager/service_manager.h b/chromium/services/service_manager/service_manager.h index 4b456bed119..d4e84aba273 100644 --- a/chromium/services/service_manager/service_manager.h +++ b/chromium/services/service_manager/service_manager.h @@ -19,16 +19,16 @@ #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/remote_set.h" +#include "sandbox/policy/sandbox_type.h" #include "services/service_manager/catalog.h" #include "services/service_manager/public/cpp/identity.h" #include "services/service_manager/public/cpp/manifest.h" #include "services/service_manager/public/cpp/service.h" -#include "services/service_manager/public/cpp/service_binding.h" +#include "services/service_manager/public/cpp/service_receiver.h" #include "services/service_manager/public/mojom/connector.mojom.h" #include "services/service_manager/public/mojom/interface_provider.mojom.h" #include "services/service_manager/public/mojom/service.mojom.h" #include "services/service_manager/public/mojom/service_manager.mojom.h" -#include "services/service_manager/sandbox/sandbox_type.h" #include "services/service_manager/service_instance_registry.h" #include "services/service_manager/service_process_host.h" @@ -194,7 +194,7 @@ class ServiceManager : public Service { const std::unique_ptr<Delegate> delegate_; - ServiceBinding service_binding_{this}; + ServiceReceiver service_receiver_{this}; // Ownership of all ServiceInstances. using InstanceMap = diff --git a/chromium/services/service_manager/service_process_host.h b/chromium/services/service_manager/service_process_host.h index 28700a4d189..d8482369915 100644 --- a/chromium/services/service_manager/service_process_host.h +++ b/chromium/services/service_manager/service_process_host.h @@ -11,9 +11,9 @@ #include "base/process/process_handle.h" #include "base/strings/string16.h" #include "mojo/public/cpp/bindings/pending_remote.h" +#include "sandbox/policy/sandbox_type.h" #include "services/service_manager/public/cpp/identity.h" #include "services/service_manager/public/mojom/service.mojom.h" -#include "services/service_manager/sandbox/sandbox_type.h" namespace service_manager { @@ -37,7 +37,7 @@ class ServiceProcessHost { using LaunchCallback = base::OnceCallback<void(base::ProcessId)>; virtual mojo::PendingRemote<mojom::Service> Launch( const Identity& identity, - SandboxType sandbox_type, + sandbox::policy::SandboxType sandbox_type, const base::string16& display_name, LaunchCallback callback) = 0; }; diff --git a/chromium/services/service_manager/service_process_launcher.cc b/chromium/services/service_manager/service_process_launcher.cc index 2a738f1ab64..c424df90cea 100644 --- a/chromium/services/service_manager/service_process_launcher.cc +++ b/chromium/services/service_manager/service_process_launcher.cc @@ -31,12 +31,12 @@ #include "mojo/public/cpp/platform/platform_channel.h" #include "mojo/public/cpp/system/core.h" #include "mojo/public/cpp/system/invitation.h" +#include "sandbox/policy/switches.h" #include "services/service_manager/public/cpp/service_executable/switches.h" #include "services/service_manager/public/mojom/service.mojom.h" -#include "services/service_manager/sandbox/switches.h" #include "services/service_manager/switches.h" -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) #include "sandbox/linux/services/namespace_sandbox.h" #endif @@ -56,7 +56,7 @@ class ServiceProcessLauncher::ProcessState base::ProcessId LaunchInBackground( const Identity& target, - SandboxType sandbox_type, + sandbox::policy::SandboxType sandbox_type, std::unique_ptr<base::CommandLine> child_command_line, mojo::PlatformChannel::HandlePassingInfo handle_passing_info, mojo::PlatformChannel channel, @@ -95,7 +95,7 @@ ServiceProcessLauncher::~ServiceProcessLauncher() { mojo::PendingRemote<mojom::Service> ServiceProcessLauncher::Start( const Identity& target, - SandboxType sandbox_type, + sandbox::policy::SandboxType sandbox_type, ProcessReadyCallback callback) { DCHECK(!state_); @@ -131,7 +131,7 @@ mojo::PendingRemote<mojom::Service> ServiceProcessLauncher::Start( if (!IsUnsandboxedSandboxType(sandbox_type)) { child_command_line->AppendSwitchASCII( - switches::kServiceSandboxType, + sandbox::policy::switches::kServiceSandboxType, StringFromUtilitySandboxType(sandbox_type)); } @@ -174,7 +174,7 @@ ServiceProcessLauncher::PassServiceRequestOnCommandLine( base::ProcessId ServiceProcessLauncher::ProcessState::LaunchInBackground( const Identity& target, - SandboxType sandbox_type, + sandbox::policy::SandboxType sandbox_type, std::unique_ptr<base::CommandLine> child_command_line, mojo::PlatformChannel::HandlePassingInfo handle_passing_info, mojo::PlatformChannel channel, @@ -217,18 +217,18 @@ base::ProcessId ServiceProcessLauncher::ProcessState::LaunchInBackground( {STDOUT_FILENO, STDOUT_FILENO}, {STDERR_FILENO, STDERR_FILENO}, }; -#if defined(OS_MACOSX) +#if defined(OS_MAC) options.fds_to_remap = fd_mapping; options.mach_ports_for_rendezvous = handle_passing_info; #else handle_passing_info.insert(handle_passing_info.end(), fd_mapping.begin(), fd_mapping.end()); options.fds_to_remap = handle_passing_info; -#endif // defined(OS_MACOSX) +#endif // defined(OS_MAC) #endif DVLOG(2) << "Launching child with command line: " << child_command_line->GetCommandLineString(); -#if defined(OS_LINUX) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) if (!IsUnsandboxedSandboxType(sandbox_type)) { child_process_ = sandbox::NamespaceSandbox::LaunchProcess(*child_command_line, options); diff --git a/chromium/services/service_manager/service_process_launcher.h b/chromium/services/service_manager/service_process_launcher.h index 1b0b04ff5c5..e45b0544aee 100644 --- a/chromium/services/service_manager/service_process_launcher.h +++ b/chromium/services/service_manager/service_process_launcher.h @@ -16,8 +16,8 @@ #include "base/process/process.h" #include "base/sequenced_task_runner.h" #include "mojo/public/cpp/bindings/pending_remote.h" +#include "sandbox/policy/sandbox_type.h" #include "services/service_manager/public/mojom/service.mojom.h" -#include "services/service_manager/sandbox/sandbox_type.h" #include "services/service_manager/service_process_launcher_delegate.h" namespace mojo { @@ -48,9 +48,10 @@ class ServiceProcessLauncher { // |Start()|s the child process; calls |DidStart()| (on the thread on which // |Start()| was called) when the child has been started (or failed to start). - mojo::PendingRemote<mojom::Service> Start(const Identity& target, - SandboxType sandbox_type, - ProcessReadyCallback callback); + mojo::PendingRemote<mojom::Service> Start( + const Identity& target, + sandbox::policy::SandboxType sandbox_type, + ProcessReadyCallback callback); // Exposed publicly for use in tests. Creates a new Service pipe, passing the // ServiceRequest end through |*invitation| with an identifier stashed in diff --git a/chromium/services/service_manager/tests/BUILD.gn b/chromium/services/service_manager/tests/BUILD.gn index 7bbd69673b5..4cef17398a9 100644 --- a/chromium/services/service_manager/tests/BUILD.gn +++ b/chromium/services/service_manager/tests/BUILD.gn @@ -31,7 +31,6 @@ test("service_manager_unittests") { "//services/service_manager/public/mojom", "//services/service_manager/tests/connect", "//services/service_manager/tests/lifecycle", - "//services/service_manager/tests/sandbox", "//services/service_manager/tests/service_manager", "//services/service_manager/tests/shutdown", "//testing/gtest", @@ -53,12 +52,12 @@ mojom("interfaces") { "test_support.test-mojom", ] - if (is_linux) { + if (is_linux || is_chromeos) { sources += [ "sandbox_status.test-mojom" ] } } -if (is_linux) { +if (is_linux || is_chromeos) { source_set("sandbox_status_service") { testonly = true sources = [ @@ -69,9 +68,9 @@ if (is_linux) { deps = [ ":interfaces", "//base", + "//sandbox/policy", "//services/service_manager/public/cpp", "//services/service_manager/public/mojom", - "//services/service_manager/sandbox:sandbox", ] } } diff --git a/chromium/services/service_manager/tests/sandbox/BUILD.gn b/chromium/services/service_manager/tests/sandbox/BUILD.gn deleted file mode 100644 index 4f8625df395..00000000000 --- a/chromium/services/service_manager/tests/sandbox/BUILD.gn +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -source_set("sandbox") { - testonly = true - sources = [ "sandbox_type_unittest.cc" ] - deps = [ - "//base", - "//base/test:test_support", - "//services/service_manager/sandbox", - "//testing/gtest", - ] - - if (is_win) { - sources += [ "sandbox_win_unittest.cc" ] - deps += [ "//sandbox/win:sandbox" ] - data = [ - "//base/test/data/pe_image/pe_image_test_32.dll", - "//base/test/data/pe_image/pe_image_test_64.dll", - "//base/test/data/pe_image/pe_image_test_arm64.dll", - ] - } -} diff --git a/chromium/services/shape_detection/BUILD.gn b/chromium/services/shape_detection/BUILD.gn index 6b5de944119..32d99821235 100644 --- a/chromium/services/shape_detection/BUILD.gn +++ b/chromium/services/shape_detection/BUILD.gn @@ -32,7 +32,7 @@ source_set("lib") { "text_detection_impl_mac.h", "text_detection_impl_mac.mm", ] - libs = [ "QuartzCore.framework" ] + frameworks = [ "QuartzCore.framework" ] } else if (is_win) { sources += [ "barcode_detection_provider_impl.cc", @@ -122,7 +122,7 @@ source_set("tests") { "text_detection_impl_win_unittest.cc", ] if (is_mac) { - libs = [ + frameworks = [ "CoreFoundation.framework", "CoreGraphics.framework", "QuartzCore.framework", diff --git a/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/BarcodeDetectionImplTest.java b/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/BarcodeDetectionImplTest.java index 4e5bb81862f..d6a7e2a68a3 100644 --- a/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/BarcodeDetectionImplTest.java +++ b/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/BarcodeDetectionImplTest.java @@ -16,6 +16,7 @@ import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate; import org.chromium.base.test.params.ParameterProvider; import org.chromium.base.test.params.ParameterSet; import org.chromium.base.test.params.ParameterizedRunner; +import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.Feature; import org.chromium.shape_detection.mojom.BarcodeDetection; import org.chromium.shape_detection.mojom.BarcodeDetectionProvider; @@ -32,6 +33,7 @@ import java.util.concurrent.TimeUnit; * Test suite for BarcodeDetectionImpl. */ @RunWith(ParameterizedRunner.class) +@Batch(Batch.UNIT_TESTS) @UseRunnerDelegate(BaseJUnit4RunnerDelegate.class) public class BarcodeDetectionImplTest { private static final org.chromium.skia.mojom.Bitmap QR_CODE_BITMAP = diff --git a/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/FaceDetectionImplTest.java b/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/FaceDetectionImplTest.java index 85a7fdce814..ca26cabe414 100644 --- a/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/FaceDetectionImplTest.java +++ b/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/FaceDetectionImplTest.java @@ -16,6 +16,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.chromium.base.test.BaseJUnit4ClassRunner; +import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.Feature; import org.chromium.shape_detection.mojom.FaceDetection; import org.chromium.shape_detection.mojom.FaceDetectionResult; @@ -28,6 +29,7 @@ import java.util.concurrent.TimeUnit; * Test suite for FaceDetectionImpl. */ @RunWith(BaseJUnit4ClassRunner.class) +@Batch(Batch.UNIT_TESTS) public class FaceDetectionImplTest { private static final org.chromium.skia.mojom.Bitmap MONA_LISA_BITMAP = TestUtils.mojoBitmapFromFile("mona_lisa.jpg"); diff --git a/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/TextDetectionImplTest.java b/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/TextDetectionImplTest.java index 953301ac851..11f8d760cff 100644 --- a/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/TextDetectionImplTest.java +++ b/chromium/services/shape_detection/android/javatests/src/org/chromium/shape_detection/TextDetectionImplTest.java @@ -11,6 +11,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.chromium.base.test.BaseJUnit4ClassRunner; +import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.Feature; import org.chromium.gfx.mojom.RectF; import org.chromium.shape_detection.mojom.TextDetection; @@ -23,6 +24,7 @@ import java.util.concurrent.TimeUnit; * Test suite for TextDetectionImpl. */ @RunWith(BaseJUnit4ClassRunner.class) +@Batch(Batch.UNIT_TESTS) public class TextDetectionImplTest { private static final String[] DETECTION_EXPECTED_TEXT = { "The quick brown fox jumped over the lazy dog.", "Helvetica Neue 36."}; diff --git a/chromium/services/shape_detection/face_detection_impl_mac_unittest.mm b/chromium/services/shape_detection/face_detection_impl_mac_unittest.mm index 40ad1e9804d..ae8a01fa039 100644 --- a/chromium/services/shape_detection/face_detection_impl_mac_unittest.mm +++ b/chromium/services/shape_detection/face_detection_impl_mac_unittest.mm @@ -148,7 +148,8 @@ TEST_P(FaceDetectionImplMacTest, CreateAndDestroy) { } } -TEST_P(FaceDetectionImplMacTest, ScanOneFace) { +// Flakily fails on multiple configurations. https://crbug.com/1107962 +TEST_P(FaceDetectionImplMacTest, DISABLED_ScanOneFace) { // Face detection test needs a GPU. if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseGpuInTests)) { diff --git a/chromium/services/shape_detection/shape_detection_service.cc b/chromium/services/shape_detection/shape_detection_service.cc index e191a7b7832..f5238afa9ea 100644 --- a/chromium/services/shape_detection/shape_detection_service.cc +++ b/chromium/services/shape_detection/shape_detection_service.cc @@ -12,7 +12,7 @@ #if defined(OS_WIN) #include "services/shape_detection/barcode_detection_provider_impl.h" #include "services/shape_detection/face_detection_provider_win.h" -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) #include <dlfcn.h> #include "services/shape_detection/barcode_detection_provider_mac.h" #include "services/shape_detection/face_detection_provider_mac.h" @@ -32,7 +32,7 @@ namespace shape_detection { ShapeDetectionService::ShapeDetectionService( mojo::PendingReceiver<mojom::ShapeDetectionService> receiver) : receiver_(this, std::move(receiver)) { -#if defined(OS_MACOSX) +#if defined(OS_MAC) if (__builtin_available(macOS 10.13, *)) { vision_framework_ = dlopen("/System/Library/Frameworks/Vision.framework/Vision", RTLD_LAZY); @@ -41,7 +41,7 @@ ShapeDetectionService::ShapeDetectionService( } ShapeDetectionService::~ShapeDetectionService() { -#if defined(OS_MACOSX) +#if defined(OS_MAC) if (__builtin_available(macOS 10.13, *)) { if (vision_framework_) dlclose(vision_framework_); @@ -55,7 +55,7 @@ void ShapeDetectionService::BindBarcodeDetectionProvider( Java_InterfaceRegistrar_bindBarcodeDetectionProvider( base::android::AttachCurrentThread(), receiver.PassPipe().release().value()); -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) BarcodeDetectionProviderMac::Create(std::move(receiver)); #else BarcodeDetectionProviderImpl::Create(std::move(receiver)); @@ -68,7 +68,7 @@ void ShapeDetectionService::BindFaceDetectionProvider( Java_InterfaceRegistrar_bindFaceDetectionProvider( base::android::AttachCurrentThread(), receiver.PassPipe().release().value()); -#elif defined(OS_MACOSX) +#elif defined(OS_MAC) FaceDetectionProviderMac::Create(std::move(receiver)); #elif defined(OS_WIN) FaceDetectionProviderWin::Create(std::move(receiver)); diff --git a/chromium/services/shape_detection/shape_detection_service.h b/chromium/services/shape_detection/shape_detection_service.h index 956e75bc31d..e9e265440f7 100644 --- a/chromium/services/shape_detection/shape_detection_service.h +++ b/chromium/services/shape_detection/shape_detection_service.h @@ -33,7 +33,7 @@ class ShapeDetectionService : public mojom::ShapeDetectionService { private: mojo::Receiver<mojom::ShapeDetectionService> receiver_; -#if defined(OS_MACOSX) +#if defined(OS_MAC) void* vision_framework_; #endif diff --git a/chromium/services/tracing/BUILD.gn b/chromium/services/tracing/BUILD.gn index bcb2fe7993d..e0d3e156649 100644 --- a/chromium/services/tracing/BUILD.gn +++ b/chromium/services/tracing/BUILD.gn @@ -86,6 +86,7 @@ source_set("tests") { "public/cpp/perfetto/producer_test_utils.h", "public/cpp/perfetto/task_runner_unittest.cc", "public/cpp/perfetto/trace_event_data_source_unittest.cc", + "public/cpp/perfetto/trace_packet_tokenizer_unittest.cc", "public/cpp/perfetto/traced_value_proto_writer_unittest.cc", "public/cpp/stack_sampling/reached_code_data_source_android_unittest.cc", "public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc", diff --git a/chromium/services/tracing/perfetto/consumer_host.cc b/chromium/services/tracing/perfetto/consumer_host.cc index 67a3d1b0e0f..7a80f83844f 100644 --- a/chromium/services/tracing/perfetto/consumer_host.cc +++ b/chromium/services/tracing/perfetto/consumer_host.cc @@ -23,9 +23,9 @@ #include "base/values.h" #include "build/build_config.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" -#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/system/wait.h" #include "services/tracing/perfetto/perfetto_service.h" +#include "services/tracing/public/cpp/perfetto/perfetto_session.h" #include "services/tracing/public/cpp/trace_event_args_allowlist.h" #include "third_party/perfetto/include/perfetto/ext/trace_processor/export_json.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/observable_events.h" @@ -217,27 +217,35 @@ ConsumerHost::TracingSession::~TracingSession() { void ConsumerHost::TracingSession::OnPerfettoEvents( const perfetto::ObservableEvents& events) { - if (!pending_enable_tracing_ack_pids_) { + if (!pending_enable_tracing_ack_pids_ || + !events.instance_state_changes_size()) { return; } for (const auto& state_change : events.instance_state_changes()) { - if (state_change.state() != - perfetto::ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STARTED) { - continue; - } - - if (state_change.data_source_name() != mojom::kTraceEventDataSourceName) { - continue; - } + DataSourceHandle handle(state_change.producer_name(), + state_change.data_source_name()); + data_source_states_[handle] = + state_change.state() == + perfetto::ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STARTED; + } + + // Data sources are first reported as being stopped before starting, so once + // all the data sources we know about have started we can declare tracing + // begun. + bool all_data_sources_started = std::all_of( + data_source_states_.cbegin(), data_source_states_.cend(), + [](std::pair<DataSourceHandle, bool> state) { return state.second; }); + if (!all_data_sources_started) + return; + for (const auto& it : data_source_states_) { // Attempt to parse the PID out of the producer name. base::ProcessId pid; - if (!PerfettoService::ParsePidFromProducerName(state_change.producer_name(), + if (!PerfettoService::ParsePidFromProducerName(it.first.producer_name(), &pid)) { continue; } - pending_enable_tracing_ack_pids_->erase(pid); } MaybeSendEnableTracingAck(); @@ -396,7 +404,11 @@ void ConsumerHost::TracingSession::DisableTracingAndEmitJson( perfetto::trace_processor::TraceProcessorStorage::CreateInstance( processor_config); - DisableTracing(); + if (tracing_enabled_) { + DisableTracing(); + } else { + host_->consumer_endpoint()->ReadBuffers(); + } } void ConsumerHost::TracingSession::ExportJson() { @@ -543,19 +555,8 @@ void ConsumerHost::TracingSession::OnTraceStats( std::move(request_buffer_usage_callback_).Run(false, 0.0f, false); return; } - - const perfetto::TraceStats::BufferStats& buf_stats = stats.buffer_stats()[0]; - size_t bytes_in_buffer = buf_stats.bytes_written() - buf_stats.bytes_read() - - buf_stats.bytes_overwritten() + - buf_stats.padding_bytes_written() - - buf_stats.padding_bytes_cleared(); - double percent_full = - bytes_in_buffer / static_cast<double>(buf_stats.buffer_size()); - percent_full = base::ClampToRange(percent_full, 0.0, 1.0); - bool data_loss = buf_stats.chunks_overwritten() > 0 || - buf_stats.chunks_discarded() > 0 || - buf_stats.abi_violations() > 0 || - buf_stats.trace_writer_packet_loss() > 0; + double percent_full = GetTraceBufferUsage(stats); + bool data_loss = HasLostData(stats); std::move(request_buffer_usage_callback_).Run(true, percent_full, data_loss); } diff --git a/chromium/services/tracing/perfetto/consumer_host.h b/chromium/services/tracing/perfetto/consumer_host.h index b8bc8e12e48..97c7e36f6f8 100644 --- a/chromium/services/tracing/perfetto/consumer_host.h +++ b/chromium/services/tracing/perfetto/consumer_host.h @@ -110,6 +110,13 @@ class ConsumerHost : public perfetto::Consumer, public mojom::ConsumerHost { base::Optional<std::set<base::ProcessId>> pending_enable_tracing_ack_pids_; base::OneShotTimer enable_tracing_ack_timer_; + struct DataSourceHandle : public std::pair<std::string, std::string> { + using std::pair<std::string, std::string>::pair; + const std::string& producer_name() const { return first; } + const std::string& data_source_name() const { return second; } + }; + std::map<DataSourceHandle, bool /*started*/> data_source_states_; + SEQUENCE_CHECKER(sequence_checker_); base::WeakPtrFactory<TracingSession> weak_factory_{this}; diff --git a/chromium/services/tracing/perfetto/perfetto_integration_unittest.cc b/chromium/services/tracing/perfetto/perfetto_integration_unittest.cc index a95c7c97a36..c5b00b316c7 100644 --- a/chromium/services/tracing/perfetto/perfetto_integration_unittest.cc +++ b/chromium/services/tracing/perfetto/perfetto_integration_unittest.cc @@ -10,12 +10,16 @@ #include "base/bind.h" #include "base/run_loop.h" #include "base/strings/strcat.h" +#include "base/test/bind_test_util.h" #include "base/test/task_environment.h" +#include "base/threading/thread.h" #include "services/tracing/perfetto/perfetto_service.h" #include "services/tracing/perfetto/producer_host.h" #include "services/tracing/perfetto/test_utils.h" +#include "services/tracing/public/cpp/perfetto/perfetto_platform.h" #include "services/tracing/public/cpp/perfetto/producer_client.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/perfetto/include/perfetto/tracing/tracing.h" #include "third_party/perfetto/protos/perfetto/common/commit_data_request.pb.h" // TODO(crbug.com/961066): Fix memory leaks in tests and re-enable on LSAN. @@ -38,9 +42,21 @@ std::string GetPerfettoProducerName() { return base::StrCat({mojom::kPerfettoProducerNamePrefix, "123"}); } +RebindableTaskRunner* GetPerfettoTaskRunner() { + static base::NoDestructor<RebindableTaskRunner> task_runner; + return task_runner.get(); +} + class PerfettoIntegrationTest : public testing::Test { public: void SetUp() override { + auto* perfetto_task_runner = GetPerfettoTaskRunner(); + auto* perfetto_platform = + PerfettoTracedProcess::Get()->perfetto_platform_for_testing(); + if (!perfetto_platform->did_start_task_runner()) + perfetto_platform->StartTaskRunner(perfetto_task_runner); + perfetto_task_runner->set_task_runner(base::ThreadTaskRunnerHandle::Get()); + PerfettoTracedProcess::ResetTaskRunnerForTesting(); PerfettoTracedProcess::Get()->ClearDataSourcesForTesting(); data_source_ = TestDataSource::CreateAndRegisterDataSource( @@ -363,6 +379,34 @@ TEST_F(PerfettoIntegrationTest, (*client2)->shared_memory_for_testing()); } +TEST_F(PerfettoIntegrationTest, PerfettoPlatformTest) { + auto* platform = + PerfettoTracedProcess::Get()->perfetto_platform_for_testing(); + auto* tls = platform->GetOrCreateThreadLocalObject(); + EXPECT_TRUE(tls); + EXPECT_EQ(tls, platform->GetOrCreateThreadLocalObject()); + + base::Thread thread("TestThread"); + thread.Start(); + thread.task_runner()->PostTask( + FROM_HERE, base::BindLambdaForTesting([&platform, &tls] { + auto* thread_tls = platform->GetOrCreateThreadLocalObject(); + EXPECT_TRUE(thread_tls); + EXPECT_NE(tls, thread_tls); + })); + thread.Stop(); +} + +TEST_F(PerfettoIntegrationTest, PerfettoClientLibraryTest) { + PerfettoTracedProcess::Get()->SetupClientLibrary(); + // Create a dummy tracing session without a real backend to check that + // the client library was initialized. + constexpr perfetto::BackendType kInvalidBackend( + static_cast<perfetto::BackendType>(1u << 31)); + auto tracing_session = perfetto::Tracing::NewTrace(kInvalidBackend); + EXPECT_TRUE(tracing_session); +} + } // namespace } // namespace tracing diff --git a/chromium/services/tracing/perfetto/perfetto_service.cc b/chromium/services/tracing/perfetto/perfetto_service.cc index de5e660bbb6..359164ed2bf 100644 --- a/chromium/services/tracing/perfetto/perfetto_service.cc +++ b/chromium/services/tracing/perfetto/perfetto_service.cc @@ -125,6 +125,15 @@ void PerfettoService::RemoveActiveServicePid(base::ProcessId pid) { } } +void PerfettoService::RemoveActiveServicePidIfNoActiveConnections( + base::ProcessId pid) { + const auto num_connections_it = num_active_connections_.find(pid); + if (num_connections_it == num_active_connections_.end() || + num_connections_it->second == 0) { + RemoveActiveServicePid(pid); + } +} + void PerfettoService::SetActiveServicePidsInitialized() { active_service_pids_initialized_ = true; for (auto* tracing_session : tracing_sessions_) { diff --git a/chromium/services/tracing/perfetto/perfetto_service.h b/chromium/services/tracing/perfetto/perfetto_service.h index edba7d6ab58..56f60c822ec 100644 --- a/chromium/services/tracing/perfetto/perfetto_service.h +++ b/chromium/services/tracing/perfetto/perfetto_service.h @@ -64,6 +64,7 @@ class PerfettoService : public mojom::PerfettoService { // actively running services (whenever a service starts or stops). void AddActiveServicePid(base::ProcessId pid); void RemoveActiveServicePid(base::ProcessId pid); + void RemoveActiveServicePidIfNoActiveConnections(base::ProcessId pid); void SetActiveServicePidsInitialized(); std::set<base::ProcessId> active_service_pids() const { diff --git a/chromium/services/tracing/perfetto/producer_host.cc b/chromium/services/tracing/perfetto/producer_host.cc index 31a907c7c8b..7d7d243213c 100644 --- a/chromium/services/tracing/perfetto/producer_host.cc +++ b/chromium/services/tracing/perfetto/producer_host.cc @@ -71,7 +71,9 @@ bool ProducerHost::Initialize( base::ProcessId pid; if (PerfettoService::ParsePidFromProducerName(name, &pid)) { bool in_process = (pid == base::Process::Current().Pid()); - if (in_process) { + // TODO(skyostil): Implement arbiter binding for the client API. + if (in_process && !base::FeatureList::IsEnabled( + features::kEnablePerfettoClientApiProducer)) { PerfettoTracedProcess::Get() ->producer_client() ->BindInProcessSharedMemoryArbiter(producer_endpoint_.get(), diff --git a/chromium/services/tracing/perfetto/system_perfetto_unittest.cc b/chromium/services/tracing/perfetto/system_perfetto_unittest.cc index f217e6d0103..0b6c066c2c2 100644 --- a/chromium/services/tracing/perfetto/system_perfetto_unittest.cc +++ b/chromium/services/tracing/perfetto/system_perfetto_unittest.cc @@ -58,30 +58,32 @@ std::string RandomASCII(size_t length) { return tmp; } -class SaveSystemProducerAndScopedRestore { +class ClearAndRestoreSystemProducerScope { public: - SaveSystemProducerAndScopedRestore() { + ClearAndRestoreSystemProducerScope() { + base::RunLoop setup_loop; PerfettoTracedProcess::GetTaskRunner()->GetOrCreateTaskRunner()->PostTask( - FROM_HERE, base::BindLambdaForTesting([this] { + FROM_HERE, base::BindLambdaForTesting([this, &setup_loop] { saved_producer_ = PerfettoTracedProcess::Get()->SetSystemProducerForTesting( - std::make_unique<DummyProducer>( - PerfettoTracedProcess::GetTaskRunner())); + nullptr); + setup_loop.Quit(); })); + setup_loop.Run(); } - ~SaveSystemProducerAndScopedRestore() { - base::RunLoop destroy; - PerfettoTracedProcess::GetTaskRunner() - ->GetOrCreateTaskRunner() - ->PostTaskAndReply( - FROM_HERE, base::BindLambdaForTesting([this]() { + ~ClearAndRestoreSystemProducerScope() { + base::RunLoop destroy_loop; + PerfettoTracedProcess::GetTaskRunner()->GetOrCreateTaskRunner()->PostTask( + FROM_HERE, + base::BindLambdaForTesting( + [this, &destroy_loop]() { PerfettoTracedProcess::Get() ->SetSystemProducerForTesting(std::move(saved_producer_)) .reset(); - }), - destroy.QuitClosure()); - destroy.Run(); + destroy_loop.Quit(); + })); + destroy_loop.Run(); } private: @@ -889,18 +891,10 @@ TEST_F(SystemPerfettoTest, SystemToLowAPILevel) { TEST_F(SystemPerfettoTest, EnabledOnDebugBuilds) { base::test::ScopedFeatureList feature_list; feature_list.InitAndDisableFeature(features::kEnablePerfettoSystemTracing); - // We have to prevent destroying the system producer because we might have - // created it on a different task environment (wrong sequence). - SaveSystemProducerAndScopedRestore saved_system_producer; - PerfettoTracedProcess::ReconstructForTesting(producer_socket_.c_str()); if (base::android::BuildInfo::GetInstance()->is_debug_android()) { - EXPECT_FALSE(PerfettoTracedProcess::Get() - ->system_producer() - ->IsDummySystemProducerForTesting()); + EXPECT_TRUE(ShouldSetupSystemTracing()); } else { - EXPECT_TRUE(PerfettoTracedProcess::Get() - ->system_producer() - ->IsDummySystemProducerForTesting()); + EXPECT_FALSE(ShouldSetupSystemTracing()); } } #endif // defined(OS_ANDROID) @@ -910,27 +904,19 @@ TEST_F(SystemPerfettoTest, RespectsFeatureList) { if (base::android::BuildInfo::GetInstance()->is_debug_android()) { // The feature list is ignored on debug android builds so we should have a // real system producer so just bail out of this test. - EXPECT_FALSE(PerfettoTracedProcess::Get() - ->system_producer() - ->IsDummySystemProducerForTesting()); + EXPECT_TRUE(ShouldSetupSystemTracing()); return; } #endif // defined(OS_ANDROID) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature(features::kEnablePerfettoSystemTracing); - PerfettoTracedProcess::ReconstructForTesting(producer_socket_.c_str()); - EXPECT_FALSE(PerfettoTracedProcess::Get() - ->system_producer() - ->IsDummySystemProducerForTesting()); + EXPECT_TRUE(ShouldSetupSystemTracing()); } { base::test::ScopedFeatureList feature_list; feature_list.InitAndDisableFeature(features::kEnablePerfettoSystemTracing); - PerfettoTracedProcess::ReconstructForTesting(producer_socket_.c_str()); - EXPECT_TRUE(PerfettoTracedProcess::Get() - ->system_producer() - ->IsDummySystemProducerForTesting()); + EXPECT_FALSE(ShouldSetupSystemTracing()); } } @@ -951,7 +937,6 @@ TEST_F(SystemPerfettoTest, RespectsFeaturePreAndroidPie) { feature_list.InitAndDisableFeature( features::kEnablePerfettoSystemTracing); } - PerfettoTracedProcess::ReconstructForTesting(producer_socket_.c_str()); std::string data_source_name = "temp_name"; @@ -1007,10 +992,26 @@ TEST_F(SystemPerfettoTest, RespectsFeaturePreAndroidPie) { return system_consumer.received_test_packets(); }; - EXPECT_EQ(1u, run_test(/* enable_feature = */ true)); EXPECT_EQ(0u, run_test(/* enable_feature = */ false)); + EXPECT_EQ(1u, run_test(/* enable_feature = */ true)); } #endif // defined(OS_ANDROID) +TEST_F(SystemPerfettoTest, SetupSystemTracing) { + ClearAndRestoreSystemProducerScope saved_system_producer; + EXPECT_FALSE(PerfettoTracedProcess::Get()->system_producer()); + PerfettoTracedProcess::Get()->SetupSystemTracing(); + EXPECT_TRUE(PerfettoTracedProcess::Get()->system_producer()); +#if defined(OS_POSIX) + EXPECT_FALSE(PerfettoTracedProcess::Get() + ->system_producer() + ->IsDummySystemProducerForTesting()); +#else // defined(OS_POSIX) + EXPECT_TRUE(PerfettoTracedProcess::Get() + ->system_producer() + ->IsDummySystemProducerForTesting()); +#endif // defined(OS_POSIX) +} + } // namespace -} // namespace tracing +} // namespace tracing
\ No newline at end of file diff --git a/chromium/services/tracing/perfetto/test_utils.cc b/chromium/services/tracing/perfetto/test_utils.cc index a8366d57819..074b557dcb2 100644 --- a/chromium/services/tracing/perfetto/test_utils.cc +++ b/chromium/services/tracing/perfetto/test_utils.cc @@ -410,6 +410,9 @@ MockProducer::MockProducer(const std::string& producer_name, MockProducer::~MockProducer() = default; +RebindableTaskRunner::RebindableTaskRunner() = default; +RebindableTaskRunner::~RebindableTaskRunner() = default; + void MockProducer::WritePacketBigly(base::OnceClosure on_write_complete) { PerfettoTracedProcess::Get() ->GetTaskRunner() @@ -420,4 +423,22 @@ void MockProducer::WritePacketBigly(base::OnceClosure on_write_complete) { std::move(on_write_complete)); } +bool RebindableTaskRunner::PostDelayedTask(const base::Location& from_here, + base::OnceClosure task, + base::TimeDelta delay) { + return task_runner_->PostDelayedTask(from_here, std::move(task), delay); +} + +bool RebindableTaskRunner::PostNonNestableDelayedTask( + const base::Location& from_here, + base::OnceClosure task, + base::TimeDelta delay) { + return task_runner_->PostNonNestableDelayedTask(from_here, std::move(task), + delay); +} + +bool RebindableTaskRunner::RunsTasksInCurrentSequence() const { + return task_runner_->RunsTasksInCurrentSequence(); +} + } // namespace tracing diff --git a/chromium/services/tracing/perfetto/test_utils.h b/chromium/services/tracing/perfetto/test_utils.h index c33d41c7f6b..7963a76a124 100644 --- a/chromium/services/tracing/perfetto/test_utils.h +++ b/chromium/services/tracing/perfetto/test_utils.h @@ -226,6 +226,31 @@ class MockProducer { std::unique_ptr<MockProducerHost> producer_host_; }; +// A proxy task runner which can be dynamically pointed to route tasks into a +// different task runner. +class RebindableTaskRunner : public base::SequencedTaskRunner { + public: + RebindableTaskRunner(); + + void set_task_runner(scoped_refptr<base::SequencedTaskRunner> task_runner) { + task_runner_ = task_runner; + } + + // base::SequecedTaskRunner implementation. + bool PostDelayedTask(const base::Location& from_here, + base::OnceClosure task, + base::TimeDelta delay) override; + bool PostNonNestableDelayedTask(const base::Location& from_here, + base::OnceClosure task, + base::TimeDelta delay) override; + bool RunsTasksInCurrentSequence() const override; + + private: + ~RebindableTaskRunner() override; + + scoped_refptr<base::SequencedTaskRunner> task_runner_; +}; + } // namespace tracing #endif // SERVICES_TRACING_PERFETTO_TEST_UTILS_H_ diff --git a/chromium/services/tracing/public/cpp/BUILD.gn b/chromium/services/tracing/public/cpp/BUILD.gn index 1cd4d42da6e..870afc29b6e 100644 --- a/chromium/services/tracing/public/cpp/BUILD.gn +++ b/chromium/services/tracing/public/cpp/BUILD.gn @@ -74,10 +74,16 @@ target(tracing_lib_type, "cpp") { "perfetto/java_heap_profiler/java_heap_profiler_android.h", "perfetto/perfetto_config.cc", "perfetto/perfetto_config.h", + "perfetto/perfetto_platform.cc", + "perfetto/perfetto_platform.h", "perfetto/perfetto_producer.cc", "perfetto/perfetto_producer.h", + "perfetto/perfetto_session.cc", + "perfetto/perfetto_session.h", "perfetto/perfetto_traced_process.cc", "perfetto/perfetto_traced_process.h", + "perfetto/perfetto_tracing_backend.cc", + "perfetto/perfetto_tracing_backend.h", "perfetto/producer_client.cc", "perfetto/producer_client.h", "perfetto/shared_memory.cc", @@ -89,6 +95,8 @@ target(tracing_lib_type, "cpp") { "perfetto/task_runner.h", "perfetto/trace_event_data_source.cc", "perfetto/trace_event_data_source.h", + "perfetto/trace_packet_tokenizer.cc", + "perfetto/trace_packet_tokenizer.h", "perfetto/trace_time.cc", "perfetto/trace_time.h", "perfetto/traced_value_proto_writer.cc", diff --git a/chromium/services/tracing/public/cpp/base_agent.h b/chromium/services/tracing/public/cpp/base_agent.h index ab3d6ecb0a4..53894b743ad 100644 --- a/chromium/services/tracing/public/cpp/base_agent.h +++ b/chromium/services/tracing/public/cpp/base_agent.h @@ -19,6 +19,7 @@ class COMPONENT_EXPORT(TRACING_CPP) BaseAgent { public: virtual ~BaseAgent(); + // May be called on any thread. virtual void GetCategories(std::set<std::string>* category_set); protected: diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_config.cc b/chromium/services/tracing/public/cpp/perfetto/perfetto_config.cc index 94edd835823..a9e1d680eaa 100644 --- a/chromium/services/tracing/public/cpp/perfetto/perfetto_config.cc +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_config.cc @@ -24,7 +24,8 @@ perfetto::TraceConfig::DataSource* AddDataSourceConfig( perfetto::TraceConfig* perfetto_config, const char* name, const std::string& chrome_config_string, - bool privacy_filtering_enabled) { + bool privacy_filtering_enabled, + bool convert_to_legacy_json) { auto* data_source = perfetto_config->add_data_sources(); auto* source_config = data_source->mutable_config(); source_config->set_name(name); @@ -32,6 +33,7 @@ perfetto::TraceConfig::DataSource* AddDataSourceConfig( auto* chrome_config = source_config->mutable_chrome_config(); chrome_config->set_trace_config(chrome_config_string); chrome_config->set_privacy_filtering_enabled(privacy_filtering_enabled); + chrome_config->set_convert_to_legacy_json(convert_to_legacy_json); return data_source; } @@ -40,7 +42,8 @@ void AddDataSourceConfigs( const base::trace_event::TraceConfig::ProcessFilterConfig& process_filters, const base::trace_event::TraceConfig& stripped_config, const std::set<std::string>& source_names, - bool privacy_filtering_enabled) { + bool privacy_filtering_enabled, + bool convert_to_legacy_json) { const std::string chrome_config_string = stripped_config.ToString(); // Capture actual trace events. @@ -48,7 +51,8 @@ void AddDataSourceConfigs( source_names.count(tracing::mojom::kTraceEventDataSourceName) == 1) { auto* trace_event_data_source = AddDataSourceConfig( perfetto_config, tracing::mojom::kTraceEventDataSourceName, - chrome_config_string, privacy_filtering_enabled); + chrome_config_string, privacy_filtering_enabled, + convert_to_legacy_json); for (auto& enabled_pid : process_filters.included_process_ids()) { *trace_event_data_source->add_producer_name_filter() = base::StrCat( {mojom::kPerfettoProducerNamePrefix, @@ -64,7 +68,8 @@ void AddDataSourceConfigs( source_names.count(tracing::mojom::kSystemTraceDataSourceName) == 1) { AddDataSourceConfig(perfetto_config, tracing::mojom::kSystemTraceDataSourceName, - chrome_config_string, privacy_filtering_enabled); + chrome_config_string, privacy_filtering_enabled, + convert_to_legacy_json); } #endif @@ -73,7 +78,8 @@ void AddDataSourceConfigs( source_names.count(tracing::mojom::kArcTraceDataSourceName) == 1) { AddDataSourceConfig(perfetto_config, tracing::mojom::kArcTraceDataSourceName, - chrome_config_string, privacy_filtering_enabled); + chrome_config_string, privacy_filtering_enabled, + convert_to_legacy_json); } #endif } @@ -82,7 +88,8 @@ void AddDataSourceConfigs( if (source_names.empty() || source_names.count(tracing::mojom::kMetaDataSourceName) == 1) { AddDataSourceConfig(perfetto_config, tracing::mojom::kMetaDataSourceName, - chrome_config_string, privacy_filtering_enabled); + chrome_config_string, privacy_filtering_enabled, + convert_to_legacy_json); } if (stripped_config.IsCategoryGroupEnabled( @@ -92,7 +99,8 @@ void AddDataSourceConfigs( source_names.count(tracing::mojom::kSamplerProfilerSourceName)); AddDataSourceConfig(perfetto_config, tracing::mojom::kSamplerProfilerSourceName, - chrome_config_string, privacy_filtering_enabled); + chrome_config_string, privacy_filtering_enabled, + convert_to_legacy_json); } if (stripped_config.IsCategoryGroupEnabled( @@ -102,14 +110,16 @@ void AddDataSourceConfigs( tracing::mojom::kJavaHeapProfilerSourceName)); AddDataSourceConfig(perfetto_config, tracing::mojom::kJavaHeapProfilerSourceName, - chrome_config_string, privacy_filtering_enabled); + chrome_config_string, privacy_filtering_enabled, + convert_to_legacy_json); } if (source_names.empty() || source_names.count(tracing::mojom::kReachedCodeProfilerSourceName) == 1) { AddDataSourceConfig(perfetto_config, tracing::mojom::kReachedCodeProfilerSourceName, - chrome_config_string, privacy_filtering_enabled); + chrome_config_string, privacy_filtering_enabled, + convert_to_legacy_json); } } @@ -117,16 +127,18 @@ void AddDataSourceConfigs( perfetto::TraceConfig GetDefaultPerfettoConfig( const base::trace_event::TraceConfig& chrome_config, - bool privacy_filtering_enabled) { - return GetPerfettoConfigWithDataSources(chrome_config, {}, - privacy_filtering_enabled); + bool privacy_filtering_enabled, + bool convert_to_legacy_json) { + return GetPerfettoConfigWithDataSources( + chrome_config, {}, privacy_filtering_enabled, convert_to_legacy_json); } perfetto::TraceConfig COMPONENT_EXPORT(TRACING_CPP) GetPerfettoConfigWithDataSources( const base::trace_event::TraceConfig& chrome_config, const std::set<std::string>& source_names, - bool privacy_filtering_enabled) { + bool privacy_filtering_enabled, + bool convert_to_legacy_json) { perfetto::TraceConfig perfetto_config; size_t size_limit = chrome_config.GetTraceBufferSizeInKb(); @@ -187,8 +199,8 @@ perfetto::TraceConfig COMPONENT_EXPORT(TRACING_CPP) stripped_config.SetTraceBufferSizeInEvents(0); AddDataSourceConfigs(&perfetto_config, chrome_config.process_filter_config(), - stripped_config, source_names, - privacy_filtering_enabled); + stripped_config, source_names, privacy_filtering_enabled, + convert_to_legacy_json); return perfetto_config; } diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_config.h b/chromium/services/tracing/public/cpp/perfetto/perfetto_config.h index 27d33a982c4..6f8b4a5b4f0 100644 --- a/chromium/services/tracing/public/cpp/perfetto/perfetto_config.h +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_config.h @@ -21,7 +21,8 @@ namespace tracing { perfetto::TraceConfig COMPONENT_EXPORT(TRACING_CPP) GetDefaultPerfettoConfig( const base::trace_event::TraceConfig& chrome_config, - bool privacy_filtering_enabled = false); + bool privacy_filtering_enabled = false, + bool convert_to_legacy_json = false); // Creates a perfetto trace config with only the data sources included in // |source_names| and enabled by |trace_config|. Passing empty set will add all @@ -31,7 +32,8 @@ perfetto::TraceConfig COMPONENT_EXPORT(TRACING_CPP) GetPerfettoConfigWithDataSources( const base::trace_event::TraceConfig& chrome_config, const std::set<std::string>& source_names, - bool privacy_filtering_enabled = false); + bool privacy_filtering_enabled = false, + bool convert_to_legacy_json = false); } // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_platform.cc b/chromium/services/tracing/public/cpp/perfetto/perfetto_platform.cc new file mode 100644 index 00000000000..d56b2280987 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_platform.cc @@ -0,0 +1,57 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/public/cpp/perfetto/perfetto_platform.h" + +#include "base/deferred_sequenced_task_runner.h" +#include "base/task/task_traits.h" +#include "base/task/thread_pool.h" +#include "services/tracing/public/cpp/perfetto/task_runner.h" + +namespace tracing { + +PerfettoPlatform::PerfettoPlatform() + : deferred_task_runner_(new base::DeferredSequencedTaskRunner()), + thread_local_object_([](void* object) { + delete static_cast<ThreadLocalObject*>(object); + }) {} + +PerfettoPlatform::~PerfettoPlatform() = default; + +void PerfettoPlatform::StartTaskRunner( + scoped_refptr<base::SequencedTaskRunner> task_runner) { + DCHECK(!did_start_task_runner_); + deferred_task_runner_->StartWithTaskRunner(task_runner); + did_start_task_runner_ = true; +} + +base::SequencedTaskRunner* PerfettoPlatform::task_runner() const { + return deferred_task_runner_.get(); +} + +PerfettoPlatform::ThreadLocalObject* +PerfettoPlatform::GetOrCreateThreadLocalObject() { + auto* object = static_cast<ThreadLocalObject*>(thread_local_object_.Get()); + if (!object) { + object = ThreadLocalObject::CreateInstance().release(); + thread_local_object_.Set(object); + } + return object; +} + +std::unique_ptr<perfetto::base::TaskRunner> PerfettoPlatform::CreateTaskRunner( + const CreateTaskRunnerArgs&) { + // We can't create a real task runner yet because the ThreadPool may not be + // initialized. Instead, we point Perfetto to a buffering task runner which + // will become active as soon as the thread pool is up (see StartTaskRunner). + return std::make_unique<PerfettoTaskRunner>(deferred_task_runner_); +} + +std::string PerfettoPlatform::GetCurrentProcessName() { + // TODO(skyostil): Unimplemented since we're not registering producers through + // the client API yet. + return ""; +} + +} // namespace tracing
\ No newline at end of file diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_platform.h b/chromium/services/tracing/public/cpp/perfetto/perfetto_platform.h new file mode 100644 index 00000000000..254ba9efca3 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_platform.h @@ -0,0 +1,44 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_PLATFORM_H_ +#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_PLATFORM_H_ + +#include "third_party/perfetto/include/perfetto/tracing/platform.h" + +#include "base/component_export.h" +#include "base/memory/scoped_refptr.h" +#include "base/threading/thread_local_storage.h" + +namespace base { +class DeferredSequencedTaskRunner; +} // namespace base + +namespace tracing { + +class COMPONENT_EXPORT(TRACING_CPP) PerfettoPlatform + : public perfetto::Platform { + public: + PerfettoPlatform(); + ~PerfettoPlatform() override; + + base::SequencedTaskRunner* task_runner() const; + bool did_start_task_runner() const { return did_start_task_runner_; } + void StartTaskRunner(scoped_refptr<base::SequencedTaskRunner>); + + // perfetto::Platform implementation: + ThreadLocalObject* GetOrCreateThreadLocalObject() override; + std::unique_ptr<perfetto::base::TaskRunner> CreateTaskRunner( + const CreateTaskRunnerArgs&) override; + std::string GetCurrentProcessName() override; + + private: + scoped_refptr<base::DeferredSequencedTaskRunner> deferred_task_runner_; + bool did_start_task_runner_ = false; + base::ThreadLocalStorage::Slot thread_local_object_; +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_PLATFORM_H_
\ No newline at end of file diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.h b/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.h index 304d0472ad3..18dfa0e40a7 100644 --- a/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.h +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.h @@ -110,7 +110,7 @@ class COMPONENT_EXPORT(TRACING_CPP) PerfettoProducer { bool IsStartupTracingActive(); - // TODO(oysteine): Find a good compromise between performance and + // TODO(crbug.com/839071): Find a good compromise between performance and // data granularity (mainly relevant to running with small buffer sizes // when we use background tracing) on Android. #if defined(OS_ANDROID) @@ -119,7 +119,7 @@ class COMPONENT_EXPORT(TRACING_CPP) PerfettoProducer { static constexpr size_t kSMBPageSizeBytes = 32 * 1024; #endif - // TODO(oysteine): Figure out a good buffer size. + // TODO(crbug.com/839071): Figure out a good buffer size. static constexpr size_t kSMBSizeBytes = 4 * 1024 * 1024; PerfettoTaskRunner* task_runner(); diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_session.cc b/chromium/services/tracing/public/cpp/perfetto/perfetto_session.cc new file mode 100644 index 00000000000..b3588ddc767 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_session.cc @@ -0,0 +1,35 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/public/cpp/perfetto/perfetto_session.h" + +#include "third_party/perfetto/protos/perfetto/common/trace_stats.gen.h" + +namespace tracing { + +double GetTraceBufferUsage(const perfetto::protos::gen::TraceStats& stats) { + // Chrome always uses a single tracing buffer. + if (stats.buffer_stats_size() != 1u) + return 0.f; + const perfetto::protos::gen::TraceStats::BufferStats& buf_stats = + stats.buffer_stats()[0]; + size_t bytes_in_buffer = buf_stats.bytes_written() - buf_stats.bytes_read() - + buf_stats.bytes_overwritten() + + buf_stats.padding_bytes_written() - + buf_stats.padding_bytes_cleared(); + return bytes_in_buffer / static_cast<double>(buf_stats.buffer_size()); +} + +bool HasLostData(const perfetto::protos::gen::TraceStats& stats) { + // Chrome always uses a single tracing buffer. + if (stats.buffer_stats_size() != 1u) + return false; + const perfetto::protos::gen::TraceStats::BufferStats& buf_stats = + stats.buffer_stats()[0]; + return buf_stats.chunks_overwritten() > 0 || + buf_stats.chunks_discarded() > 0 || buf_stats.abi_violations() > 0 || + buf_stats.trace_writer_packet_loss() > 0; +} + +} // namespace tracing
\ No newline at end of file diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_session.h b/chromium/services/tracing/public/cpp/perfetto/perfetto_session.h new file mode 100644 index 00000000000..9f6e0dd6779 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_session.h @@ -0,0 +1,31 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_SESSION_H_ +#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_SESSION_H_ + +#include <set> +#include <string> + +#include "base/component_export.h" + +namespace perfetto { +namespace protos { +namespace gen { +class TraceStats; +} // namespace gen +} // namespace protos +} // namespace perfetto + +namespace tracing { + +// Helpers for deriving information from Perfetto's tracing session statistics. +double COMPONENT_EXPORT(TRACING_CPP) + GetTraceBufferUsage(const perfetto::protos::gen::TraceStats&); +bool COMPONENT_EXPORT(TRACING_CPP) + HasLostData(const perfetto::protos::gen::TraceStats&); + +} // namespace tracing + +#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_SESSION_H_
\ No newline at end of file diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc b/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc index b9d5d7cfff3..4bc5ac15547 100644 --- a/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc @@ -5,7 +5,6 @@ #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" #include "base/bind_helpers.h" -#include "base/feature_list.h" #include "base/no_destructor.h" #include "base/run_loop.h" #include "base/sequenced_task_runner.h" @@ -14,11 +13,15 @@ #include "base/trace_event/trace_log.h" #include "build/build_config.h" #include "services/tracing/public/cpp/perfetto/dummy_producer.h" +#include "services/tracing/public/cpp/perfetto/perfetto_platform.h" +#include "services/tracing/public/cpp/perfetto/perfetto_tracing_backend.h" #include "services/tracing/public/cpp/perfetto/producer_client.h" #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h" #include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h" #include "services/tracing/public/cpp/trace_startup.h" #include "services/tracing/public/cpp/tracing_features.h" +#include "services/tracing/public/mojom/tracing_service.mojom.h" +#include "third_party/perfetto/include/perfetto/tracing/tracing.h" #if defined(OS_POSIX) // As per 'gn help check': @@ -38,10 +41,8 @@ namespace { std::unique_ptr<SystemProducer> NewSystemProducer(PerfettoTaskRunner* runner, const char* socket_name) { #if defined(OS_POSIX) - if (ShouldSetupSystemTracing()) { - DCHECK(socket_name); - return std::make_unique<PosixSystemProducer>(socket_name, runner); - } + DCHECK(socket_name); + return std::make_unique<PosixSystemProducer>(socket_name, runner); #endif // defined(OS_POSIX) return std::make_unique<DummyProducer>(runner); } @@ -80,16 +81,32 @@ PerfettoTracedProcess* PerfettoTracedProcess::Get() { } PerfettoTracedProcess::PerfettoTracedProcess() - : PerfettoTracedProcess(MaybeSocket()) {} - -PerfettoTracedProcess::PerfettoTracedProcess(const char* system_socket) : producer_client_(std::make_unique<ProducerClient>(GetTaskRunner())), - system_producer_(NewSystemProducer(GetTaskRunner(), system_socket)) { + platform_(std::make_unique<PerfettoPlatform>()), + tracing_backend_(std::make_unique<PerfettoTracingBackend>(*this)) { DETACH_FROM_SEQUENCE(sequence_checker_); } PerfettoTracedProcess::~PerfettoTracedProcess() {} +void PerfettoTracedProcess::SetConsumerConnectionFactory( + ConsumerConnectionFactory factory, + scoped_refptr<base::SequencedTaskRunner> task_runner) { + consumer_connection_factory_ = factory; + consumer_connection_task_runner_ = task_runner; +} + +void PerfettoTracedProcess::ConnectProducer( + mojo::PendingRemote<mojom::PerfettoService> perfetto_service) { + if (base::FeatureList::IsEnabled( + features::kEnablePerfettoClientApiProducer)) { + DCHECK(pending_producer_callback_); + std::move(pending_producer_callback_).Run(std::move(perfetto_service)); + } else { + producer_client_->Connect(std::move(perfetto_service)); + } +} + void PerfettoTracedProcess::ClearDataSourcesForTesting() { base::AutoLock lock(data_sources_lock_); data_sources_.clear(); @@ -120,6 +137,37 @@ void PerfettoTracedProcess::DeleteSoonForTesting( FROM_HERE, std::move(perfetto_traced_process)); } +void PerfettoTracedProcess::CreateProducerConnection( + base::OnceCallback<void(mojo::PendingRemote<mojom::PerfettoService>)> + callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Perfetto will attempt to create the producer connection as soon as the + // client library is initialized, which is before we have a a connection to + // the tracing service. Store the connection callback until ConnectProducer() + // is called. + DCHECK(!pending_producer_callback_); + pending_producer_callback_ = std::move(callback); +} + +void PerfettoTracedProcess::CreateConsumerConnection( + base::OnceCallback<void(mojo::PendingRemote<mojom::ConsumerHost>)> + callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + consumer_connection_task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + [](ConsumerConnectionFactory factory, + base::OnceCallback<void(mojo::PendingRemote<mojom::ConsumerHost>)> + callback) { + auto& tracing_service = factory(); + mojo::PendingRemote<mojom::ConsumerHost> consumer_host_remote; + tracing_service.BindConsumerHost( + consumer_host_remote.InitWithNewPipeAndPassReceiver()); + std::move(callback).Run(std::move(consumer_host_remote)); + }, + consumer_connection_factory_, std::move(callback))); +} + // We never destroy the taskrunner as we may need it for cleanup // of TraceWriters in TLS, which could happen after the PerfettoTracedProcess // is deleted. @@ -149,34 +197,14 @@ void PerfettoTracedProcess::ResetTaskRunnerForTesting( PerfettoTracedProcess::Get() ->producer_client() ->ResetSequenceForTesting(); - PerfettoTracedProcess::Get() - ->system_producer() - ->ResetSequenceForTesting(); + if (PerfettoTracedProcess::Get()->system_producer()) { + PerfettoTracedProcess::Get() + ->system_producer() + ->ResetSequenceForTesting(); + } })); } -// static -void PerfettoTracedProcess::ReconstructForTesting(const char* socket_name) { - base::RunLoop finished_reconstruction_runloop; - // The Get() call ensures that the construct has run and any required tasks - // have been completed before this lambda below is executed. - Get()->GetTaskRunner()->GetOrCreateTaskRunner()->PostTask( - FROM_HERE, - base::BindOnce( - [](base::OnceClosure on_finish, const char* socket_name) { - PerfettoTracedProcess::Get()->~PerfettoTracedProcess(); - new (PerfettoTracedProcess::Get()) PerfettoTracedProcess( - socket_name ? socket_name : MaybeSocket()); - // The constructor and destructor needs to be run on the proper - // sequence, but the constructor also calls PostTask so we need to - // place a task afterwards to block until everything is initialized. - GetTaskRunner()->GetOrCreateTaskRunner()->PostTaskAndReply( - FROM_HERE, base::DoNothing(), std::move(on_finish)); - }, - finished_reconstruction_runloop.QuitClosure(), socket_name)); - finished_reconstruction_runloop.Run(); -} - void PerfettoTracedProcess::AddDataSource(DataSourceBase* data_source) { bool inserted; { @@ -194,8 +222,10 @@ void PerfettoTracedProcess::AddDataSource(DataSourceBase* data_source) { PerfettoTracedProcess::Get(); traced_process->producer_client()->NewDataSourceAdded( data_source); - traced_process->system_producer()->NewDataSourceAdded( - data_source); + if (traced_process->system_producer()) { + traced_process->system_producer() + ->NewDataSourceAdded(data_source); + } }, base::Unretained(data_source))); } @@ -212,7 +242,7 @@ bool PerfettoTracedProcess::SetupStartupTracing( const base::trace_event::TraceConfig& trace_config, bool privacy_filtering_enabled) { if (producer_client_->IsTracingActive() || - system_producer_->IsTracingActive()) { + (system_producer_ && system_producer_->IsTracingActive())) { LOG(WARNING) << "Cannot setup startup tracing - tracing is already active"; return false; } @@ -225,6 +255,14 @@ bool PerfettoTracedProcess::SetupStartupTracing( return true; } +void PerfettoTracedProcess::SetupClientLibrary() { + perfetto::TracingInitArgs init_args; + init_args.platform = platform_.get(); + init_args.custom_backend = tracing_backend_.get(); + init_args.backends |= perfetto::kCustomBackend; + perfetto::Tracing::Initialize(init_args); +} + void PerfettoTracedProcess::OnThreadPoolAvailable() { // Create our task runner now, so that ProducerClient/SystemProducer are // notified about future data source registrations and schedule any necessary @@ -232,19 +270,45 @@ void PerfettoTracedProcess::OnThreadPoolAvailable() { GetTaskRunner()->GetOrCreateTaskRunner(); producer_client_->OnThreadPoolAvailable(); + if (system_producer_) + system_producer_->OnThreadPoolAvailable(); + if (!platform_->did_start_task_runner()) + platform_->StartTaskRunner(GetTaskRunner()->GetOrCreateTaskRunner()); +} + +void PerfettoTracedProcess::SetupSystemTracing( + base::Optional<const char*> system_socket) { + // Note: Not checking for a valid sequence here so that we don't inadvertently + // bind this object on the wrong sequence during early initialization. + DCHECK(!system_producer_); + system_producer_ = NewSystemProducer( + GetTaskRunner(), system_socket ? *system_socket : MaybeSocket()); + // If the thread pool is available, register all the known data sources with + // the system producer too. + if (!GetTaskRunner()->HasTaskRunner()) + return; system_producer_->OnThreadPoolAvailable(); + GetTaskRunner()->GetOrCreateTaskRunner()->PostTask( + FROM_HERE, base::BindOnce([]() { + PerfettoTracedProcess* traced_process = PerfettoTracedProcess::Get(); + base::AutoLock lock(traced_process->data_sources_lock_); + for (auto* data_source : traced_process->data_sources_) { + traced_process->system_producer()->NewDataSourceAdded(data_source); + } + })); } bool PerfettoTracedProcess::CanStartTracing( PerfettoProducer* producer, base::OnceCallback<void()> start_tracing) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(producer); // If the |producer| asking is the local producer_client_ it has priority so // even if the other endpoint is tracing shut down the other endpoint and let // the |producer_client_| go. The system Producer will periodically attempt to // reconnect if we call DisconnectWithReply(). if (producer == producer_client_.get()) { - if (system_producer_->IsTracingActive()) { + if (system_producer_ && system_producer_->IsTracingActive()) { system_producer_->DisconnectWithReply(std::move(start_tracing)); return true; } diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.h b/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.h index 03f02da072c..be55393a4d2 100644 --- a/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.h +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.h @@ -10,6 +10,7 @@ #include "base/sequence_checker.h" #include "base/sequenced_task_runner.h" #include "base/synchronization/lock.h" +#include "services/tracing/public/cpp/perfetto/perfetto_tracing_backend.h" #include "services/tracing/public/cpp/perfetto/task_runner.h" namespace base { @@ -19,7 +20,11 @@ class TraceConfig; } // namespace base namespace tracing { +namespace mojom { +class TracingService; +} // namespace mojom +class PerfettoPlatform; class PerfettoProducer; class ProducerClient; class SystemProducer; @@ -35,7 +40,8 @@ class SystemProducer; // * Register the data source with Perfetto in ProducerHost::OnConnect. // * Construct the new implementation when requested to // in PerfettoProducer::StartDataSource. -class COMPONENT_EXPORT(TRACING_CPP) PerfettoTracedProcess final { +class COMPONENT_EXPORT(TRACING_CPP) PerfettoTracedProcess final + : public PerfettoTracingBackend::Delegate { public: // If not noted otherwise, a DataSourceBase's methods are only called on // PerfettoTracedProcess::GetTaskRunner()'s sequence. @@ -94,10 +100,21 @@ class COMPONENT_EXPORT(TRACING_CPP) PerfettoTracedProcess final { // Returns the process-wide instance of the PerfettoTracedProcess. static PerfettoTracedProcess* Get(); + // Provide a factory for lazily creating mojo consumer connections to the + // tracing service. Allows using Perfetto's Client API for recording traces. + using ConsumerConnectionFactory = mojom::TracingService& (*)(); + void SetConsumerConnectionFactory(ConsumerConnectionFactory, + scoped_refptr<base::SequencedTaskRunner>); + + // Connect the current process to the mojo trace producer API. Depending on + // the configuration, this will either set up the Perfetto Client API or the + // legacy TraceLog to become the trace producer for this process. + void ConnectProducer(mojo::PendingRemote<mojom::PerfettoService>); + ProducerClient* producer_client() const; - SystemProducer* system_producer() const; + SystemProducer* system_producer() const; // May be null. - ~PerfettoTracedProcess(); + ~PerfettoTracedProcess() override; // Returns the task runner used by any Perfetto service. Can be called on any // thread. @@ -119,9 +136,19 @@ class COMPONENT_EXPORT(TRACING_CPP) PerfettoTracedProcess final { const base::trace_event::TraceConfig&, bool privacy_filtering_enabled); + // Initialize the Perfetto client library (i.e., perfetto::Tracing) for this + // process. Should be called early during startup. + void SetupClientLibrary(); + // Called on the process's main thread once the thread pool is ready. void OnThreadPoolAvailable(); + // Called to initialize system tracing, i.e., connecting to a system Perfetto + // daemon as a producer. If |system_socket| isn't provided, Perfetto's default + // socket name is used. + void SetupSystemTracing(base::Optional<const char*> system_socket = + base::Optional<const char*>()); + // If the provided |producer| can begin tracing then |start_tracing| will be // invoked (unless cancelled by the Perfetto service) at some point later // using the GetTaskRunner()'s sequence and this function will return true. @@ -150,12 +177,22 @@ class COMPONENT_EXPORT(TRACING_CPP) PerfettoTracedProcess final { void ClearDataSourcesForTesting(); static void DeleteSoonForTesting(std::unique_ptr<PerfettoTracedProcess>); - static void ReconstructForTesting(const char* system_socket); + + PerfettoPlatform* perfetto_platform_for_testing() const { + return platform_.get(); + } + + // PerfettoTracingBackend::Delegate implementation. + void CreateProducerConnection( + base::OnceCallback<void(mojo::PendingRemote<mojom::PerfettoService>)>) + override; + void CreateConsumerConnection( + base::OnceCallback<void(mojo::PendingRemote<mojom::ConsumerHost>)>) + override; protected: // protected for testing. PerfettoTracedProcess(); - explicit PerfettoTracedProcess(const char* system_socket); private: friend class base::NoDestructor<PerfettoTracedProcess>; @@ -173,6 +210,16 @@ class COMPONENT_EXPORT(TRACING_CPP) PerfettoTracedProcess final { // calls will be noops. std::unique_ptr<SystemProducer> system_producer_; + // Platform implementation for the Perfetto client library. + std::unique_ptr<PerfettoPlatform> platform_; + std::unique_ptr<PerfettoTracingBackend> tracing_backend_; + + scoped_refptr<base::SequencedTaskRunner> consumer_connection_task_runner_; + ConsumerConnectionFactory consumer_connection_factory_; + + base::OnceCallback<void(mojo::PendingRemote<mojom::PerfettoService>)> + pending_producer_callback_; + SEQUENCE_CHECKER(sequence_checker_); DISALLOW_COPY_AND_ASSIGN(PerfettoTracedProcess); }; diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_tracing_backend.cc b/chromium/services/tracing/public/cpp/perfetto/perfetto_tracing_backend.cc new file mode 100644 index 00000000000..808421e4cc7 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_tracing_backend.cc @@ -0,0 +1,654 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/public/cpp/perfetto/perfetto_tracing_backend.h" + +#include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" +#include "base/memory/weak_ptr.h" +#include "base/task/post_task.h" +#include "build/build_config.h" +#include "mojo/public/cpp/system/data_pipe_drainer.h" +#include "services/tracing/public/cpp/perfetto/perfetto_producer.h" +#include "services/tracing/public/cpp/perfetto/shared_memory.h" +#include "services/tracing/public/cpp/perfetto/trace_event_data_source.h" +#include "services/tracing/public/cpp/perfetto/trace_packet_tokenizer.h" +#include "services/tracing/public/mojom/perfetto_service.mojom.h" +#include "services/tracing/public/mojom/tracing_service.mojom.h" +#include "third_party/perfetto/include/perfetto/base/task_runner.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/commit_data_request.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/consumer.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/producer.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/shared_memory_arbiter.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_stats.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/tracing_service.h" +#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" + +namespace tracing { +namespace { + +// TODO(crbug.com/83907): Find a good compromise between performance and +// data granularity (mainly relevant to running with small buffer sizes +// when we use background tracing) on Android. +#if defined(OS_ANDROID) +constexpr size_t kDefaultSMBPageSizeBytes = 4 * 1024; +#else +constexpr size_t kDefaultSMBPageSizeBytes = 32 * 1024; +#endif + +// TODO(crbug.com/839071): Figure out a good buffer size. +constexpr size_t kDefaultSMBSizeBytes = 4 * 1024 * 1024; + +} // namespace + +// Implements Perfetto's ProducerEndpoint interface on top of the +// PerfettoService mojo service. +class ProducerEndpoint : public perfetto::ProducerEndpoint, + public mojom::ProducerClient { + public: + ProducerEndpoint(PerfettoTracingBackend::Delegate& delegate, + const std::string& producer_name, + perfetto::Producer* producer, + perfetto::base::TaskRunner* producer_task_runner, + size_t shmem_size_hint_bytes, + size_t shmem_page_size_hint_bytes) + : producer_(producer) { + // The producers's task runner must match where the endpoint is + // constructed; otherwise we can't safely use a weak pointer to send + // events back to the producer. |producer_task_runner| is also assumed to + // outlive this endpoint. + DCHECK(producer_task_runner->RunsTasksOnCurrentThread()); + delegate.CreateProducerConnection( + base::BindOnce(&ProducerEndpoint::OnConnected, + weak_factory_.GetWeakPtr(), producer_task_runner, + shmem_size_hint_bytes, shmem_page_size_hint_bytes)); + } + + ~ProducerEndpoint() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + producer_->OnDisconnect(); + } + + // perfetto::ProducerEndpoint implementation: + void RegisterDataSource( + const perfetto::DataSourceDescriptor& descriptor) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + producer_host_->RegisterDataSource(descriptor); + } + + void UnregisterDataSource(const std::string& name) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Implement data source unregistering. + NOTREACHED(); + } + + void RegisterTraceWriter(uint32_t writer_id, + uint32_t target_buffer) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + producer_host_->RegisterTraceWriter(writer_id, target_buffer); + } + + void UnregisterTraceWriter(uint32_t writer_id) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + producer_host_->UnregisterTraceWriter(writer_id); + } + + void CommitData(const perfetto::CommitDataRequest& commit, + CommitDataCallback callback = {}) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + auto commit_callback = + callback + ? base::BindOnce( + [](perfetto::ProducerEndpoint::CommitDataCallback callback) { + callback(); + }, + callback) + : mojom::ProducerHost::CommitDataCallback(); + // We need to make sure the CommitData IPC is sent off without triggering + // any trace events, as that could stall waiting for SMB chunks to be freed + // up which requires the tracing service to receive the IPC. + if (!TraceEventDataSource::GetThreadIsInTraceEventTLS()->Get()) { + AutoThreadLocalBoolean thread_is_in_trace_event( + TraceEventDataSource::GetThreadIsInTraceEventTLS()); + producer_host_->CommitData(commit, std::move(commit_callback)); + return; + } + producer_host_->CommitData(commit, std::move(commit_callback)); + } + + perfetto::SharedMemory* shared_memory() const override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return shared_memory_.get(); + } + + size_t shared_buffer_page_size_kb() const override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return shared_buffer_page_size_kb_; + } + + std::unique_ptr<perfetto::TraceWriter> CreateTraceWriter( + perfetto::BufferID target_buffer, + perfetto::BufferExhaustedPolicy) override { + // Can be called from any thread. + // Chromium uses BufferExhaustedPolicy::kDrop to avoid stalling trace + // writers when the chunks in the SMB are exhausted. Stalling could + // otherwise lead to deadlocks in chromium, because a stalled mojo IPC + // thread could prevent CommitRequest messages from reaching the perfetto + // service. + return MaybeSharedMemoryArbiter()->CreateTraceWriter( + target_buffer, perfetto::BufferExhaustedPolicy::kDrop); + } + + perfetto::SharedMemoryArbiter* MaybeSharedMemoryArbiter() override { + // Can be called from any thread. + return shared_memory_arbiter_.get(); + } + + bool IsShmemProvidedByProducer() const override { return true; } + + void NotifyFlushComplete(perfetto::FlushRequestID flush_request_id) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + perfetto::CommitDataRequest commit; + commit.set_flush_request_id(flush_request_id); + producer_host_->CommitData(commit, + mojom::ProducerHost::CommitDataCallback()); + } + + void NotifyDataSourceStarted( + perfetto::DataSourceInstanceID instance_id) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + auto it = ds_start_callbacks_.find(instance_id); + if (it == ds_start_callbacks_.end()) + return; + auto callback = std::move(it->second); + ds_start_callbacks_.erase(it); + std::move(callback).Run(); + } + + void NotifyDataSourceStopped( + perfetto::DataSourceInstanceID instance_id) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + auto it = ds_stop_callbacks_.find(instance_id); + if (it == ds_stop_callbacks_.end()) + return; + auto callback = std::move(it->second); + ds_stop_callbacks_.erase(it); + std::move(callback).Run(); + } + + void ActivateTriggers(const std::vector<std::string>&) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Implement. + NOTREACHED(); + } + + void Sync(std::function<void()> callback) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Implement. + NOTREACHED(); + } + + // mojom::ProducerClient implementation. + void OnTracingStart() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + producer_->OnTracingSetup(); + } + + void StartDataSource(uint64_t id, + const perfetto::DataSourceConfig& data_source_config, + StartDataSourceCallback callback) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(ds_start_callbacks_.find(id) == ds_start_callbacks_.end()); + ds_start_callbacks_[id] = std::move(callback); + auto it_and_inserted = ds_instances_.insert(id); + DCHECK(it_and_inserted.second); + producer_->SetupDataSource(id, data_source_config); + producer_->StartDataSource(id, data_source_config); + } + + void StopDataSource(uint64_t id, StopDataSourceCallback callback) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(ds_stop_callbacks_.find(id) == ds_stop_callbacks_.end()); + ds_stop_callbacks_[id] = std::move(callback); + ds_instances_.erase(id); + producer_->StopDataSource(id); + } + + void Flush(uint64_t flush_request_id, + const std::vector<uint64_t>& data_source_ids) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + producer_->Flush(flush_request_id, data_source_ids.data(), + data_source_ids.size()); + } + + void ClearIncrementalState() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + std::vector<perfetto::DataSourceInstanceID> ds_instances; + for (auto id : ds_instances_) + ds_instances.push_back(id); + producer_->ClearIncrementalState(ds_instances.data(), ds_instances.size()); + } + + private: + struct EndpointBindings { + mojo::PendingReceiver<mojom::ProducerClient> client_receiver; + mojo::PendingRemote<mojom::ProducerHost> host_remote; + std::unique_ptr<MojoSharedMemory> shared_memory; + }; + + static void OnConnected( + base::WeakPtr<ProducerEndpoint> weak_endpoint, + perfetto::base::TaskRunner* producer_task_runner, + size_t shmem_size_bytes, + size_t shmem_page_size_bytes, + mojo::PendingRemote<mojom::PerfettoService> perfetto_service) { + // Called on the connection's sequence -- |this| may have been deleted. + auto bindings = std::make_unique<EndpointBindings>(); + + // TODO(skyostil): Make it possible to pass the shared memory allocation + // from the client library to here (for startup tracing). + if (!shmem_size_bytes) + shmem_size_bytes = kDefaultSMBSizeBytes; + if (!shmem_page_size_bytes) + shmem_page_size_bytes = kDefaultSMBPageSizeBytes; + bindings->shared_memory = + std::make_unique<MojoSharedMemory>(shmem_size_bytes); + + if (!bindings->shared_memory->shared_buffer().is_valid()) { + // There's no way to do tracing after an SMB allocation failure, so let's + // disconnect Perfetto. + // TODO(skyostil): Record failure in UMA. + producer_task_runner->PostTask([weak_endpoint] { + if (!weak_endpoint) + return; + DCHECK_CALLED_ON_VALID_SEQUENCE(weak_endpoint->sequence_checker_); + weak_endpoint->producer_->OnDisconnect(); + }); + return; + } + + mojo::PendingRemote<mojom::ProducerClient> client; + bindings->client_receiver = client.InitWithNewPipeAndPassReceiver(); + mojo::Remote<mojom::PerfettoService>(std::move(perfetto_service)) + ->ConnectToProducerHost( + std::move(client), + bindings->host_remote.InitWithNewPipeAndPassReceiver(), + bindings->shared_memory->Clone(), shmem_page_size_bytes); + + // Bind the interfaces on Perfetto's sequence so we can avoid extra thread + // hops. + producer_task_runner->PostTask([weak_endpoint, producer_task_runner, + raw_bindings = bindings.release(), + shmem_size_bytes, shmem_page_size_bytes]() { + auto bindings = base::WrapUnique(raw_bindings); + // Called on the endpoint's sequence -- |endpoint| may be deleted. + if (!weak_endpoint) + return; + weak_endpoint->BindConnectionOnSequence( + producer_task_runner, std::move(bindings), shmem_size_bytes, + shmem_page_size_bytes); + }); + } + + void BindConnectionOnSequence( + perfetto::base::TaskRunner* producer_task_runner, + std::unique_ptr<EndpointBindings> bindings, + size_t shmem_size_bytes, + size_t shmem_page_size_bytes) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + producer_host_.Bind(std::move(bindings->host_remote)); + producer_host_.reset_on_disconnect(); + receiver_ = std::make_unique<mojo::Receiver<mojom::ProducerClient>>( + this, std::move(bindings->client_receiver)); + receiver_->set_disconnect_handler(base::BindOnce( + [](ProducerEndpoint* endpoint) { endpoint->receiver_->reset(); }, + base::Unretained(this))); + + shared_memory_ = std::move(bindings->shared_memory); + shared_buffer_page_size_kb_ = shmem_size_bytes / 1024; + shared_memory_arbiter_ = perfetto::SharedMemoryArbiter::CreateInstance( + shared_memory_.get(), shmem_page_size_bytes, this, + producer_task_runner); + producer_->OnConnect(); + } + + SEQUENCE_CHECKER(sequence_checker_); + + perfetto::Producer* const producer_; + + base::flat_map<perfetto::DataSourceInstanceID, StartDataSourceCallback> + ds_start_callbacks_; + base::flat_map<perfetto::DataSourceInstanceID, StopDataSourceCallback> + ds_stop_callbacks_; + base::flat_set<perfetto::DataSourceInstanceID> ds_instances_; + + std::unique_ptr<mojo::Receiver<mojom::ProducerClient>> receiver_; + mojo::Remote<mojom::ProducerHost> producer_host_; + + size_t shared_buffer_page_size_kb_ = 0; + + // Accessed on arbitrary threads after setup. + std::unique_ptr<MojoSharedMemory> shared_memory_; + std::unique_ptr<perfetto::SharedMemoryArbiter> shared_memory_arbiter_; + + base::WeakPtrFactory<ProducerEndpoint> weak_factory_{this}; +}; + +// Implements Perfetto's ConsumerEndpoint interface on top of the +// ConsumerHost mojo service. +class ConsumerEndpoint : public perfetto::ConsumerEndpoint, + public mojom::TracingSessionClient, + public mojo::DataPipeDrainer::Client { + public: + ConsumerEndpoint(PerfettoTracingBackend::Delegate& delegate, + perfetto::Consumer* consumer, + perfetto::base::TaskRunner* consumer_task_runner) + : consumer_{consumer} { + // To avoid extra thread hops, the consumer's task runner must match where + // the endpoint is constructed. + DCHECK(consumer_task_runner->RunsTasksOnCurrentThread()); + delegate.CreateConsumerConnection( + base::BindOnce(&ConsumerEndpoint::OnConnected, + weak_factory_.GetWeakPtr(), consumer_task_runner)); + } + + ~ConsumerEndpoint() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + consumer_->OnDisconnect(); + } + + // perfetto::ConsumerEndpoint implementation. + void EnableTracing(const perfetto::TraceConfig& trace_config, + perfetto::base::ScopedFile file) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!file); // Direct tracing to a file isn't supported. + trace_config_ = trace_config; + if (!trace_config.deferred_start()) + StartTracing(); + } + + void ChangeTraceConfig(const perfetto::TraceConfig& trace_config) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + trace_config_ = trace_config; + tracing_session_host_->ChangeTraceConfig(trace_config); + } + + void StartTracing() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Don't hardcode the session's priority. + consumer_host_->EnableTracing( + tracing_session_host_.BindNewPipeAndPassReceiver(), + tracing_session_client_.BindNewPipeAndPassRemote(), trace_config_, + tracing::mojom::TracingClientPriority::kUserInitiated); + tracing_session_host_.set_disconnect_handler(base::BindOnce( + &ConsumerEndpoint::OnTracingFailed, base::Unretained(this))); + tracing_session_client_.set_disconnect_handler(base::BindOnce( + &ConsumerEndpoint::OnTracingFailed, base::Unretained(this))); + } + + void DisableTracing() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + tracing_session_host_->DisableTracing(); + } + + void Flush(uint32_t timeout_ms, FlushCallback callback) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Implement flushing. + NOTREACHED(); + } + + void ReadBuffers() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!tracing_session_host_ || !tracing_session_client_.is_bound()) { + OnTracingFailed(); + return; + } + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + MojoResult result = + mojo::CreateDataPipe(nullptr, &producer_handle, &consumer_handle); + if (result != MOJO_RESULT_OK) { + OnTracingFailed(); + return; + } + drainer_ = std::make_unique<mojo::DataPipeDrainer>( + this, std::move(consumer_handle)); + trace_data_complete_ = false; + read_buffers_complete_ = false; + + // Convert to legacy JSON if needed. + for (const auto& data_source : trace_config_.data_sources()) { + if (data_source.config().has_chrome_config() && + data_source.config().chrome_config().convert_to_legacy_json()) { + tracing_session_host_->DisableTracingAndEmitJson( + /*agent_label_filter=*/"", std::move(producer_handle), + /*privacy_filter_enabled=*/false, + base::BindOnce(&ConsumerEndpoint::OnReadBuffersComplete, + base::Unretained(this))); + return; + } + } + + tokenizer_ = std::make_unique<TracePacketTokenizer>(); + tracing_session_host_->ReadBuffers( + std::move(producer_handle), + base::BindOnce(&ConsumerEndpoint::OnReadBuffersComplete, + base::Unretained(this))); + } + + void FreeBuffers() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + tracing_session_host_.reset(); + tracing_session_client_.reset(); + drainer_.reset(); + tokenizer_.reset(); + } + + void Detach(const std::string& key) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + NOTREACHED() << "Detaching session not supported"; + } + + void Attach(const std::string& key) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + NOTREACHED() << "Attaching session not supported"; + } + + void GetTraceStats() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Capturing |this| is safe, because the callback will be cancelled if the + // connection terminates. + tracing_session_host_->RequestBufferUsage(base::BindOnce( + [](ConsumerEndpoint* endpoint, bool success, float percent_full, + bool data_loss) { + DCHECK_CALLED_ON_VALID_SEQUENCE(endpoint->sequence_checker_); + // Since we only get a few basic stats from the service, synthesize + // just enough trace statistics to be able to show a buffer usage + // indicator. + // TODO(skyostil): Plumb the entire TraceStats objects from the + // service to avoid this. + uint64_t buffer_size = 0; + if (endpoint->trace_config_.buffers_size() >= 1) { + buffer_size = endpoint->trace_config_.buffers()[0].size_kb() * 1024; + } + perfetto::TraceStats stats; + if (success && buffer_size) { + auto* buf_stats = stats.add_buffer_stats(); + buf_stats->set_buffer_size(buffer_size); + buf_stats->set_bytes_written( + static_cast<uint64_t>(percent_full * buffer_size)); + if (data_loss) + buf_stats->set_trace_writer_packet_loss(1); + } + endpoint->consumer_->OnTraceStats(success, stats); + }, + base::Unretained(this))); + } + + void ObserveEvents(uint32_t events_mask) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!(events_mask & + ~perfetto::ObservableEvents::TYPE_DATA_SOURCES_INSTANCES)); + observed_events_mask_ = events_mask; + } + + void QueryServiceState(QueryServiceStateCallback) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Implement service state querying. + NOTREACHED(); + } + + void QueryCapabilities(QueryCapabilitiesCallback) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Implement capability querying. + NOTREACHED(); + } + + // tracing::mojom::TracingSessionClient implementation: + void OnTracingEnabled() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Wire up full data source state. For now Perfetto just + // needs to know all data sources have started. + if (observed_events_mask_ & + perfetto::ObservableEvents::TYPE_DATA_SOURCES_INSTANCES) { + perfetto::ObservableEvents events; + events.add_instance_state_changes()->set_state( + perfetto::ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STARTED); + consumer_->OnObservableEvents(events); + } + } + + void OnTracingDisabled() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Wire up full data source state. For now Perfetto just + // needs to know all data sources have stopped. + if (observed_events_mask_ & + perfetto::ObservableEvents::TYPE_DATA_SOURCES_INSTANCES) { + perfetto::ObservableEvents events; + events.add_instance_state_changes()->set_state( + perfetto::ObservableEvents::DATA_SOURCE_INSTANCE_STATE_STOPPED); + consumer_->OnObservableEvents(events); + } + consumer_->OnTracingDisabled(); + } + + // mojo::DataPipeDrainer::Client implementation: + void OnDataAvailable(const void* data, size_t num_bytes) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (tokenizer_) { + // Protobuf-format data. + auto packets = + tokenizer_->Parse(reinterpret_cast<const uint8_t*>(data), num_bytes); + if (!packets.empty()) + consumer_->OnTraceData(std::move(packets), /*has_more=*/true); + } else { + // Legacy JSON-format data. + std::vector<perfetto::TracePacket> packets; + packets.emplace_back(); + packets.back().AddSlice(data, num_bytes); + consumer_->OnTraceData(std::move(packets), /*has_more=*/true); + } + } + + void OnDataComplete() override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (tokenizer_) { + DCHECK(!tokenizer_->has_more()); + tokenizer_.reset(); + } + trace_data_complete_ = true; + MaybeFinishTraceData(); + } + + private: + static void OnConnected( + base::WeakPtr<ConsumerEndpoint> weak_endpoint, + perfetto::base::TaskRunner* consumer_task_runner, + mojo::PendingRemote<mojom::ConsumerHost> consumer_host_remote) { + // Called on the connection's sequence -- |this| may have been deleted. + auto wrapped_remote = + std::make_unique<mojo::PendingRemote<mojom::ConsumerHost>>( + std::move(consumer_host_remote)); + + // Bind the interfaces on Perfetto's sequence so we can avoid extra thread + // hops. + consumer_task_runner->PostTask([weak_endpoint, + raw_remote = wrapped_remote.release()]() { + auto consumer_host_remote = base::WrapUnique(raw_remote); + // Called on the endpoint's sequence -- |endpoint| may be deleted. + if (!weak_endpoint) + return; + weak_endpoint->BindConnectionOnSequence(std::move(*consumer_host_remote)); + }); + } + + void BindConnectionOnSequence( + mojo::PendingRemote<mojom::ConsumerHost> consumer_host_remote) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + consumer_host_.Bind(std::move(consumer_host_remote)); + consumer_host_.reset_on_disconnect(); + consumer_->OnConnect(); + } + + void OnTracingFailed() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(skyostil): Inform the crew. + tracing_session_host_.reset(); + tracing_session_client_.reset(); + drainer_.reset(); + tokenizer_.reset(); + } + + void OnReadBuffersComplete() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + read_buffers_complete_ = true; + MaybeFinishTraceData(); + } + + void MaybeFinishTraceData() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!read_buffers_complete_ || !trace_data_complete_) + return; + consumer_->OnTraceData({}, /*has_more=*/false); + } + + SEQUENCE_CHECKER(sequence_checker_); + perfetto::Consumer* const consumer_; + mojo::Remote<tracing::mojom::ConsumerHost> consumer_host_; + mojo::Remote<tracing::mojom::TracingSessionHost> tracing_session_host_; + mojo::Receiver<tracing::mojom::TracingSessionClient> tracing_session_client_{ + this}; + std::unique_ptr<mojo::DataPipeDrainer> drainer_; + perfetto::TraceConfig trace_config_; + + std::unique_ptr<TracePacketTokenizer> tokenizer_; + bool read_buffers_complete_ = false; + bool trace_data_complete_ = false; + uint32_t observed_events_mask_ = 0; + + base::WeakPtrFactory<ConsumerEndpoint> weak_factory_{this}; +}; + +PerfettoTracingBackend::PerfettoTracingBackend(Delegate& delegate) + : delegate_(delegate) {} + +PerfettoTracingBackend::~PerfettoTracingBackend() = default; +PerfettoTracingBackend::Delegate::~Delegate() = default; + +std::unique_ptr<perfetto::ConsumerEndpoint> +PerfettoTracingBackend::ConnectConsumer(const ConnectConsumerArgs& args) { + return std::make_unique<ConsumerEndpoint>(delegate_, args.consumer, + args.task_runner); +} + +std::unique_ptr<perfetto::ProducerEndpoint> +PerfettoTracingBackend::ConnectProducer(const ConnectProducerArgs& args) { + return std::make_unique<ProducerEndpoint>( + delegate_, args.producer_name, args.producer, args.task_runner, + args.shmem_size_hint_bytes, args.shmem_page_size_hint_bytes); +} + +} // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_tracing_backend.h b/chromium/services/tracing/public/cpp/perfetto/perfetto_tracing_backend.h new file mode 100644 index 00000000000..87e7d3c901a --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_tracing_backend.h @@ -0,0 +1,67 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_TRACING_BACKEND_H_ +#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_TRACING_BACKEND_H_ + +#include "base/sequence_checker.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "third_party/perfetto/include/perfetto/tracing/tracing_backend.h" + +namespace tracing { +namespace mojom { +class ConsumerHost; +class PerfettoService; +} // namespace mojom + +// The Perfetto tracing backend mediates between the Perfetto client library and +// the mojo-based tracing service. It allows any process to emit trace data +// through Perfetto and privileged processes (i.e., the browser) to start +// tracing sessions and read back the resulting data. +// +// Perfetto : Tracing backend : Tracing service +// : : +// : mojo +// calls : .------------------. : .--------------. +// .---------->| ConsumerEndpoint |<--->| ConsumerHost | +// .--------------. : `------------------' : `--------------' +// | TracingMuxer | : : +// `--------------' : .------------------. : .--------------. +// `---------->| ProducerEndpoint |<--->| ProducerHost | +// : `------------------' : `--------------' +// : : +class PerfettoTracingBackend : public perfetto::TracingBackend { + public: + class Delegate { + public: + virtual ~Delegate(); + + // Called to establish a consumer connection to the tracing service. The + // callback may be called on an arbitrary sequence. + virtual void CreateConsumerConnection( + base::OnceCallback<void(mojo::PendingRemote<mojom::ConsumerHost>)>) = 0; + + // Called to establish a producer connection to the tracing service. The + // callback may be called on an arbitrary sequence. + virtual void CreateProducerConnection( + base::OnceCallback< + void(mojo::PendingRemote<mojom::PerfettoService>)>) = 0; + }; + + explicit PerfettoTracingBackend(Delegate&); + ~PerfettoTracingBackend() override; + + // perfetto::TracingBackend implementation: + std::unique_ptr<perfetto::ProducerEndpoint> ConnectProducer( + const ConnectProducerArgs&) override; + std::unique_ptr<perfetto::ConsumerEndpoint> ConnectConsumer( + const ConnectConsumerArgs&) override; + + private: + Delegate& delegate_; +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_PERFETTO_PUBLIC_CPP_PERFETTO_TRACING_BACKEND_H_ diff --git a/chromium/services/tracing/public/cpp/perfetto/producer_client.cc b/chromium/services/tracing/public/cpp/perfetto/producer_client.cc index edbda2af892..8df88a0c9e9 100644 --- a/chromium/services/tracing/public/cpp/perfetto/producer_client.cc +++ b/chromium/services/tracing/public/cpp/perfetto/producer_client.cc @@ -7,7 +7,6 @@ #include <utility> #include "base/bind.h" -#include "base/debug/dump_without_crashing.h" #include "base/metrics/histogram_functions.h" #include "base/no_destructor.h" #include "base/process/process.h" @@ -398,18 +397,6 @@ bool ProducerClient::InitSharedMemoryIfNeeded() { base::UmaHistogramBoolean(kSharedBufferIsValidMetricName, valid); if (!valid) { - // TODO(crbug.com/1074115): Investigate why Breakpad doesn't seem to - // generate reports on some ChromeOS boards. - if (pre_dump_error_callback_) { - pre_dump_error_callback_.Run(); - } - - bool dump_with_crashing_result = base::debug::DumpWithoutCrashing(); - - if (post_dump_error_callback_) { - post_dump_error_callback_.Run(dump_with_crashing_result); - } - LOG(ERROR) << "Failed to create tracing SMB"; shared_memory_.reset(); return false; @@ -469,12 +456,4 @@ void ProducerClient::NotifyDataSourceFlushComplete( } } -void ProducerClient::SetBufferAllocationFailureCallbacks( - base::Closure pre_dump_error_callback, - base::Callback<void(bool dump_result)> post_dump_error_callback) { - base::AutoLock lock(lock_); - pre_dump_error_callback_ = std::move(pre_dump_error_callback); - post_dump_error_callback_ = std::move(post_dump_error_callback); -} - } // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/producer_client.h b/chromium/services/tracing/public/cpp/perfetto/producer_client.h index 63a010f43ac..5f6e5c4d376 100644 --- a/chromium/services/tracing/public/cpp/perfetto/producer_client.h +++ b/chromium/services/tracing/public/cpp/perfetto/producer_client.h @@ -109,13 +109,6 @@ class COMPONENT_EXPORT(TRACING_CPP) ProducerClient mojo::PendingRemote<mojom::ProducerHost>); perfetto::SharedMemory* shared_memory_for_testing(); - // Callbacks that are triggered if this class cannot allocate its shared - // buffer. For error reporting. The second callback is called after - // DumpWithoutCrashing is called, and includes the result of that call. - void SetBufferAllocationFailureCallbacks( - base::Closure pre_dump_error_callback, - base::Callback<void(bool dump_result)> post_dump_error_callback); - protected: // Protected for testing. Returns false if SMB creation failed. bool InitSharedMemoryIfNeeded(); @@ -150,12 +143,6 @@ class COMPONENT_EXPORT(TRACING_CPP) ProducerClient std::unique_ptr<perfetto::SharedMemoryArbiter> shared_memory_arbiter_ GUARDED_BY(lock_); - // See ProducerClient::SetBufferAllocationFailureCallbacks for details of - // what these callbacks mean. - base::Closure pre_dump_error_callback_ GUARDED_BY(lock_); - base::Callback<void(bool dump_without_crashing_result)> - post_dump_error_callback_ GUARDED_BY(lock_); - // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory<ProducerClient> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(ProducerClient); diff --git a/chromium/services/tracing/public/cpp/perfetto/producer_test_utils.cc b/chromium/services/tracing/public/cpp/perfetto/producer_test_utils.cc index 7ec9798e28a..f9fc7a85d29 100644 --- a/chromium/services/tracing/public/cpp/perfetto/producer_test_utils.cc +++ b/chromium/services/tracing/public/cpp/perfetto/producer_test_utils.cc @@ -13,6 +13,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/perfetto/include/perfetto/ext/base/utils.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h" +#include "third_party/perfetto/include/perfetto/protozero/root_message.h" namespace tracing { @@ -41,7 +42,7 @@ class DummyTraceWriter : public perfetto::TraceWriter { uint64_t written() const override { return 0u; } private: - perfetto::protos::pbzero::TracePacket trace_packet_; + protozero::RootMessage<perfetto::protos::pbzero::TracePacket> trace_packet_; protozero::ScatteredStreamWriterNullDelegate delegate_; protozero::ScatteredStreamWriter stream_; }; diff --git a/chromium/services/tracing/public/cpp/perfetto/producer_test_utils.h b/chromium/services/tracing/public/cpp/perfetto/producer_test_utils.h index bbab71f84da..a00d767070b 100644 --- a/chromium/services/tracing/public/cpp/perfetto/producer_test_utils.h +++ b/chromium/services/tracing/public/cpp/perfetto/producer_test_utils.h @@ -13,6 +13,7 @@ #include "services/tracing/public/cpp/perfetto/producer_client.h" #include "services/tracing/public/cpp/perfetto/task_runner.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h" +#include "third_party/perfetto/include/perfetto/protozero/root_message.h" #include "third_party/perfetto/include/perfetto/protozero/scattered_stream_null_delegate.h" #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pb.h" #include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h" @@ -64,7 +65,7 @@ class TestProducerClient : public ProducerClient { legacy_metadata_packets_; std::vector<std::unique_ptr<perfetto::protos::TracePacket>> proto_metadata_packets_; - perfetto::protos::pbzero::TracePacket trace_packet_; + protozero::RootMessage<perfetto::protos::pbzero::TracePacket> trace_packet_; protozero::ScatteredStreamWriterNullDelegate delegate_; protozero::ScatteredStreamWriter stream_; std::unique_ptr<PerfettoTaskRunner> main_thread_task_runner_; diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.cc index 88506ec5207..ea32fad393a 100644 --- a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.cc +++ b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.cc @@ -301,7 +301,7 @@ void TraceEventMetadataSource::GenerateMetadata( trace_packet->set_timestamp_clock_id(kTraceClockId); auto* chrome_metadata = trace_packet->set_chrome_metadata(); for (auto& generator : *proto_generators) { - generator.Run(chrome_metadata, privacy_filtering_enabled_); + generator.Run(chrome_metadata, privacy_filtering_enabled); } if (privacy_filtering_enabled) { @@ -463,11 +463,19 @@ void TraceEventDataSource::RegisterStartupHooks() { &TraceEventDataSource::OnAddTypedTraceEvent); } -void TraceEventDataSource::RegisterWithTraceLog() { +void TraceEventDataSource::RegisterWithTraceLog( + const base::trace_event::TraceConfig& trace_config) { TraceLog::GetInstance()->SetAddTraceEventOverrides( &TraceEventDataSource::OnAddLegacyTraceEvent, &TraceEventDataSource::FlushCurrentThread, &TraceEventDataSource::OnUpdateDuration); + + if (trace_config.IsCategoryGroupEnabled( + TRACE_DISABLED_BY_DEFAULT("histogram_samples"))) { + base::StatisticsRecorder::SetGlobalSampleCallback( + &TraceEventDataSource::OnMetricsSampleCallback); + } + base::AutoLock l(lock_); is_enabled_ = true; } @@ -577,7 +585,6 @@ void TraceEventDataSource::SetupStartupTracing( trace_writer_ = CreateTraceWriterLocked(); } EmitTrackDescriptor(); - RegisterWithTraceLog(); base::trace_event::TraceConfig config_for_trace_log(trace_config); // Perfetto backend configures buffer sizes when tracing is started in the @@ -590,6 +597,9 @@ void TraceEventDataSource::SetupStartupTracing( if (!trace_config.event_filters().empty()) { modes |= base::trace_event::TraceLog::FILTERING_MODE; } + + RegisterWithTraceLog(config_for_trace_log); + base::trace_event::TraceLog::GetInstance()->SetEnabled(trace_config, modes); } @@ -741,6 +751,9 @@ void TraceEventDataSource::StartTracingInternal( } } + auto trace_config = + TraceConfig(data_source_config.chrome_config().trace_config()); + // SetupStartupTracing() will not setup a new startup session after we set // |producer_| above, so accessing |startup_tracing_active| outside the lock // is safe. @@ -752,24 +765,17 @@ void TraceEventDataSource::StartTracingInternal( producer->BindStartupTargetBuffer(session_id, data_source_config.target_buffer()); } else { - RegisterWithTraceLog(); + RegisterWithTraceLog(trace_config); } // We emit the track/process descriptor another time even if we were // previously startup tracing, because the process name may have changed. EmitTrackDescriptor(); - auto trace_config = - TraceConfig(data_source_config.chrome_config().trace_config()); TraceLog::GetInstance()->SetEnabled(trace_config, TraceLog::RECORDING_MODE); ResetHistograms(trace_config); if (trace_config.IsCategoryGroupEnabled( - TRACE_DISABLED_BY_DEFAULT("histogram_samples"))) { - base::StatisticsRecorder::SetGlobalSampleCallback( - &TraceEventDataSource::OnMetricsSampleCallback); - } - if (trace_config.IsCategoryGroupEnabled( TRACE_DISABLED_BY_DEFAULT("user_action_samples"))) { auto task_runner = base::GetRecordActionTaskRunner(); if (task_runner) { @@ -1017,26 +1023,29 @@ void TraceEventDataSource::OnMetricsSampleCallback( const char* histogram_name, uint64_t name_hash, base::HistogramBase::Sample sample) { - // TODO(oysteine): Write an interned histogram name during local dev tracing - // when we're less space constrained. TRACE_EVENT_INSTANT( TRACE_DISABLED_BY_DEFAULT("histogram_samples"), "HistogramSample", TRACE_EVENT_SCOPE_THREAD, [&](perfetto::EventContext ctx) { + bool privacy_filtering_enabled = + TraceEventDataSource::GetInstance()->IsPrivacyFilteringEnabled(); perfetto::protos::pbzero::ChromeHistogramSample* new_sample = ctx.event()->set_chrome_histogram_sample(); new_sample->set_name_hash(name_hash); new_sample->set_sample(sample); + if (!privacy_filtering_enabled) { + new_sample->set_name(histogram_name); + } }); } void TraceEventDataSource::OnUserActionSampleCallback( const std::string& action, base::TimeTicks action_time) { - bool privacy_filtering_enabled = - TraceEventDataSource::GetInstance()->IsPrivacyFilteringEnabled(); TRACE_EVENT_INSTANT( TRACE_DISABLED_BY_DEFAULT("user_action_samples"), "UserAction", TRACE_EVENT_SCOPE_GLOBAL, [&](perfetto::EventContext ctx) { + bool privacy_filtering_enabled = + TraceEventDataSource::GetInstance()->IsPrivacyFilteringEnabled(); perfetto::protos::pbzero::ChromeUserEvent* new_sample = ctx.event()->set_chrome_user_event(); if (!privacy_filtering_enabled) { @@ -1081,6 +1090,10 @@ void TraceEventDataSource::ReturnTraceWriter( } void TraceEventDataSource::EmitTrackDescriptor() { + // Prevent reentrancy into tracing code (flushing the trace writer sends a + // mojo message which can result in additional trace events). + AutoThreadLocalBoolean thread_is_in_trace_event(GetThreadIsInTraceEventTLS()); + AutoLockWithDeferredTaskPosting lock(lock_); int process_id = TraceLog::GetInstance()->process_id(); diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.h index ad6943fb834..9de35c76c4f 100644 --- a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.h +++ b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.h @@ -204,7 +204,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceEventDataSource PerfettoProducer* producer_client, const perfetto::DataSourceConfig& data_source_config); - void RegisterWithTraceLog(); + void RegisterWithTraceLog(const base::trace_event::TraceConfig& trace_config); void OnStopTracingDone(); std::unique_ptr<perfetto::TraceWriter> CreateTraceWriterLocked(); diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_packet_tokenizer.cc b/chromium/services/tracing/public/cpp/perfetto/trace_packet_tokenizer.cc new file mode 100644 index 00000000000..25f13212b05 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/trace_packet_tokenizer.cc @@ -0,0 +1,99 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/public/cpp/perfetto/trace_packet_tokenizer.h" + +#include "base/check.h" +#include "base/check_op.h" +#include "base/macros.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h" + +namespace tracing { +namespace { + +static constexpr uint8_t kPacketTag = + protozero::proto_utils::MakeTagLengthDelimited( + perfetto::TracePacket::kPacketFieldNumber); + +} // namespace + +TracePacketTokenizer::TracePacketTokenizer() = default; +TracePacketTokenizer::~TracePacketTokenizer() = default; +TracePacketTokenizer::Packet::Packet() = default; +TracePacketTokenizer::Packet::~Packet() = default; + +std::vector<perfetto::TracePacket> TracePacketTokenizer::Parse( + const uint8_t* data, + size_t size) { + std::vector<perfetto::TracePacket> packets; + const uint8_t* data_end = data + size; + const uint8_t* packet_ptr = data; + + // Only one fragmented packet can be finalized per call to Parse(), so clear + // any previous one. + assembled_packet_ = Packet(); + + while (packet_ptr < data_end) { + // First parse the packet header, i.e., the one byte field tag and the + // variable sized packet length field. + if (!next_packet_.parsed_size) { + // Parse the field tag. + auto prev_header_size = next_packet_.header->size(); + size_t bytes_to_copy = kMaxHeaderSize - prev_header_size; + next_packet_.header->insert( + next_packet_.header->end(), packet_ptr, + std::min(packet_ptr + bytes_to_copy, data_end)); + DCHECK(next_packet_.header->size() <= kMaxHeaderSize); + if (next_packet_.header->size() < kMinHeaderSize) { + // Not enough data -- try again later. + return packets; + } + DCHECK_EQ(kPacketTag, next_packet_.header[0]); + + // Parse the size field. + const auto* size_begin = &next_packet_.header[1]; + const auto* size_end = protozero::proto_utils::ParseVarInt( + size_begin, &*next_packet_.header->end(), &next_packet_.parsed_size); + size_t size_field_size = size_end - size_begin; + if (!size_field_size) { + // Size field overflows to next chunk. Try again later. + return packets; + } + // Find the start of the packet data after the size field. + packet_ptr += sizeof(kPacketTag) + size_field_size - prev_header_size; + } + + // We've now parsed the the proto preamble and the size field for our + // packet. Let's see if the packet fits completely into this chunk. + DCHECK(next_packet_.parsed_size); + size_t remaining_size = + next_packet_.parsed_size - next_packet_.partial_data->size(); + if (packet_ptr + remaining_size > data_end) { + // Save remaining bytes into overflow buffer and try again later. + next_packet_.partial_data->insert(next_packet_.partial_data->end(), + packet_ptr, data_end); + return packets; + } + + // The packet is now complete. It can have a slice overflowing from the + // previous chunk(s) as well a a slice in the current chunk. + packets.emplace_back(); + if (!next_packet_.partial_data->empty()) { + DCHECK(assembled_packet_.partial_data->empty()); + assembled_packet_ = std::move(next_packet_); + packets.back().AddSlice(&assembled_packet_.partial_data[0], + assembled_packet_.partial_data->size()); + } + CHECK_LE(packet_ptr + remaining_size, data_end); + packets.back().AddSlice(packet_ptr, remaining_size); + packet_ptr += remaining_size; + + // Start a new packet. + next_packet_ = Packet(); + } + DCHECK_EQ(packet_ptr, data_end); + return packets; +} + +} // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_packet_tokenizer.h b/chromium/services/tracing/public/cpp/perfetto/trace_packet_tokenizer.h new file mode 100644 index 00000000000..62f7f503f4c --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/trace_packet_tokenizer.h @@ -0,0 +1,78 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACE_PACKET_TOKENIZER_H_ +#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACE_PACKET_TOKENIZER_H_ + +#include <vector> + +#include "base/component_export.h" +#include "base/containers/stack_container.h" +#include "third_party/perfetto/include/perfetto/protozero/proto_utils.h" + +namespace perfetto { +class TracePacket; +} // namespace perfetto + +namespace tracing { + +// Converts between a raw stream of perfetto::TracePacket bytes and a tokenized +// vector of delineated packets. +// +// The tracing service provides us with serialized proto bytes, while the +// Perfetto consumer expects a vector of TracePackets (i.e., without the field +// number and size header) without partial or truncated packets. To translate +// between the two, we find the position of each TracePacket and construct a +// vector pointing to the original data at the corresponding offsets. +// +// To make matters more complicated, mojo can split the data chunks +// arbitrarily, including in the middle of trace packets. To work around +// this, we tokenize as much data as we can and buffer any unprocessed bytes as +// long as needed. +class COMPONENT_EXPORT(TRACING_CPP) TracePacketTokenizer { + public: + TracePacketTokenizer(); + ~TracePacketTokenizer(); + + // Given a chunk of trace data, tokenize as many trace packets as possible + // (could be zero) and return the result. Note that the tokenized packets have + // pointers to |data| as well as |this|, so they won't be valid after another + // call to Parse(). + std::vector<perfetto::TracePacket> Parse(const uint8_t* data, size_t size); + + // Returns |true| if there is more data left to be consumed in the tokenizer. + bool has_more() const { + return !next_packet_.header->empty() || next_packet_.parsed_size || + !next_packet_.partial_data->empty(); + } + + private: + static constexpr size_t kMinHeaderSize = sizeof(uint8_t) + sizeof(uint8_t); + static constexpr size_t kMaxHeaderSize = + sizeof(uint8_t) + protozero::proto_utils::kMessageLengthFieldSize; + + struct Packet { + Packet(); + ~Packet(); + + // Most trace packets are very small, so avoid heap allocations in the + // common case where one packet straddles the boundary between chunks. + base::StackVector<uint8_t, 64> partial_data; + + uint64_t parsed_size = 0; + base::StackVector<uint8_t, kMaxHeaderSize> header; + }; + + // Packet currently being parsed. + Packet next_packet_; + + // Most recently completed packet which spanned multiple chunks. Kept as a + // member so that the memory remains valid while the caller is processing the + // results. + Packet assembled_packet_; +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACE_PACKET_TOKENIZER_H_ diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_packet_tokenizer_unittest.cc b/chromium/services/tracing/public/cpp/perfetto/trace_packet_tokenizer_unittest.cc new file mode 100644 index 00000000000..9afa3bde2a2 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/trace_packet_tokenizer_unittest.cc @@ -0,0 +1,143 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/public/cpp/perfetto/trace_packet_tokenizer.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h" +#include "third_party/perfetto/protos/perfetto/trace/trace.pb.h" +#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h" + +#include <list> + +namespace tracing { +namespace { + +constexpr char kTestString[] = "This little packet went to the market"; + +class TracePacketTokenizerTest : public testing::Test { + protected: + // Parse a trace chunk using an indirect memory allocation so ASAN can detect + // any out-of-bounds reads. + std::vector<perfetto::TracePacket> ParseChunk(const uint8_t* data, + size_t size) { + input_chunks_.emplace_back(data, data + size); + auto& it = input_chunks_.back(); + return tokenizer_.Parse(it.data(), it.size()); + } + + void Reset() { + input_chunks_.clear(); + tokenizer_ = TracePacketTokenizer(); + } + + TracePacketTokenizer& tokenizer() { return tokenizer_; } + + private: + std::list<std::vector<uint8_t>> input_chunks_; + TracePacketTokenizer tokenizer_; +}; + +TEST_F(TracePacketTokenizerTest, Basic) { + perfetto::protos::Trace trace; + auto* packet = trace.add_packet(); + packet->mutable_for_testing()->set_str(kTestString); + auto packet_data = trace.SerializeAsString(); + + auto packets = ParseChunk( + reinterpret_cast<const uint8_t*>(packet_data.data()), packet_data.size()); + EXPECT_EQ(1u, packets.size()); + perfetto::protos::TracePacket parsed_packet; + EXPECT_TRUE( + parsed_packet.ParseFromString(packets[0].GetRawBytesForTesting())); + EXPECT_EQ(kTestString, parsed_packet.for_testing().str()); + EXPECT_FALSE(tokenizer().has_more()); +} + +TEST_F(TracePacketTokenizerTest, PartialParse) { + perfetto::protos::Trace trace; + auto* packet = trace.add_packet(); + packet->mutable_for_testing()->set_str(kTestString); + auto packet_data = trace.SerializeAsString(); + + auto packets = + ParseChunk(reinterpret_cast<const uint8_t*>(packet_data.data()), + packet_data.size() / 2); + EXPECT_TRUE(packets.empty()); + EXPECT_TRUE(tokenizer().has_more()); + + packets = ParseChunk(reinterpret_cast<const uint8_t*>(packet_data.data() + + packet_data.size() / 2), + packet_data.size() / 2); + EXPECT_EQ(1u, packets.size()); + perfetto::protos::TracePacket parsed_packet; + EXPECT_TRUE( + parsed_packet.ParseFromString(packets[0].GetRawBytesForTesting())); + EXPECT_EQ(kTestString, parsed_packet.for_testing().str()); + EXPECT_FALSE(tokenizer().has_more()); +} + +TEST_F(TracePacketTokenizerTest, MultiplePackets) { + constexpr size_t kPacketCount = 32; + perfetto::protos::Trace trace; + for (size_t i = 0; i < kPacketCount; i++) { + auto* packet = trace.add_packet(); + packet->set_timestamp(i); + packet->mutable_for_testing()->set_str(kTestString); + } + auto packet_data = trace.SerializeAsString(); + + auto packets = ParseChunk( + reinterpret_cast<const uint8_t*>(packet_data.data()), packet_data.size()); + EXPECT_EQ(kPacketCount, packets.size()); + + for (size_t i = 0; i < kPacketCount; i++) { + perfetto::protos::TracePacket parsed_packet; + EXPECT_TRUE( + parsed_packet.ParseFromString(packets[i].GetRawBytesForTesting())); + EXPECT_EQ(i, parsed_packet.timestamp()); + EXPECT_EQ(kTestString, parsed_packet.for_testing().str()); + } + EXPECT_FALSE(tokenizer().has_more()); +} + +TEST_F(TracePacketTokenizerTest, Fragmentation) { + constexpr size_t kPacketCount = 17; + perfetto::protos::Trace trace; + for (size_t i = 0; i < kPacketCount; i++) { + auto* packet = trace.add_packet(); + packet->set_timestamp(i + 1); + packet->mutable_for_testing()->set_str(kTestString); + } + auto packet_data = trace.SerializeAsString(); + + for (size_t chunk_size = 1; chunk_size < packet_data.size(); chunk_size++) { + size_t packet_count = 0; + for (size_t offset = 0; offset < packet_data.size(); offset += chunk_size) { + const auto* chunk_start = + reinterpret_cast<const uint8_t*>(packet_data.data()) + offset; + const auto* chunk_end = + std::min(chunk_start + chunk_size, + reinterpret_cast<const uint8_t*>(&*packet_data.end())); + auto packets = ParseChunk(chunk_start, chunk_end - chunk_start); + if (packets.empty()) + continue; + packet_count += packets.size(); + + for (auto& packet : packets) { + perfetto::protos::TracePacket parsed_packet; + EXPECT_TRUE( + parsed_packet.ParseFromString(packet.GetRawBytesForTesting())); + EXPECT_GT(parsed_packet.timestamp(), 0u); + EXPECT_EQ(kTestString, parsed_packet.for_testing().str()); + } + } + EXPECT_EQ(kPacketCount, packet_count); + EXPECT_FALSE(tokenizer().has_more()); + Reset(); + } +} + +} // namespace +} // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_time.cc b/chromium/services/tracing/public/cpp/perfetto/trace_time.cc index a8319c7ff06..0fc56753416 100644 --- a/chromium/services/tracing/public/cpp/perfetto/trace_time.cc +++ b/chromium/services/tracing/public/cpp/perfetto/trace_time.cc @@ -12,7 +12,8 @@ namespace tracing { int64_t TraceBootTicksNow() { // On Windows and Mac, TRACE_TIME_TICKS_NOW() behaves like boottime already. -#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) || \ + defined(OS_FUCHSIA) struct timespec ts; int res = clock_gettime(CLOCK_BOOTTIME, &ts); if (res != -1) diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_time.h b/chromium/services/tracing/public/cpp/perfetto/trace_time.h index 34a60632258..c2ef710d758 100644 --- a/chromium/services/tracing/public/cpp/perfetto/trace_time.h +++ b/chromium/services/tracing/public/cpp/perfetto/trace_time.h @@ -10,7 +10,8 @@ namespace tracing { -#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) +#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) || \ + defined(OS_FUCHSIA) // Linux, Android, and Fuchsia all use CLOCK_MONOTONIC. See crbug.com/166153 // about efforts to unify base::TimeTicks across all platforms. constexpr perfetto::protos::pbzero::BuiltinClock kTraceClockId = diff --git a/chromium/services/tracing/public/cpp/perfetto/traced_value_proto_writer.cc b/chromium/services/tracing/public/cpp/perfetto/traced_value_proto_writer.cc index 863fb3ed28c..4eb9fd2e4f5 100644 --- a/chromium/services/tracing/public/cpp/perfetto/traced_value_proto_writer.cc +++ b/chromium/services/tracing/public/cpp/perfetto/traced_value_proto_writer.cc @@ -11,6 +11,7 @@ #include "base/trace_event/trace_event.h" #include "base/trace_event/traced_value.h" #include "third_party/perfetto/include/perfetto/protozero/message_handle.h" +#include "third_party/perfetto/include/perfetto/protozero/root_message.h" #include "third_party/perfetto/include/perfetto/protozero/scattered_heap_buffer.h" #include "third_party/perfetto/include/perfetto/protozero/scattered_stream_writer.h" #include "third_party/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.h" @@ -241,7 +242,7 @@ class ProtoWriter final : public TracedValue::Writer { std::stack<ProtoValueHandle> node_stack_; - ProtoValue proto_; + protozero::RootMessage<ProtoValue> proto_; protozero::ScatteredHeapBuffer buffer_; protozero::ScatteredStreamWriter stream_; }; diff --git a/chromium/services/tracing/public/cpp/perfetto/traced_value_proto_writer_unittest.cc b/chromium/services/tracing/public/cpp/perfetto/traced_value_proto_writer_unittest.cc index 1eaec32c640..301d52abb72 100644 --- a/chromium/services/tracing/public/cpp/perfetto/traced_value_proto_writer_unittest.cc +++ b/chromium/services/tracing/public/cpp/perfetto/traced_value_proto_writer_unittest.cc @@ -107,19 +107,12 @@ bool IsValue(const NestedValue* proto_value, const char* value) { } NestedValue GetProtoFromTracedValue(TracedValue* traced_value) { - protozero::ScatteredHeapBuffer buffer(100); - protozero::ScatteredStreamWriter stream(&buffer); - perfetto::protos::pbzero::DebugAnnotation proto; - proto.Reset(&stream); - buffer.set_writer(&stream); - - PerfettoProtoAppender proto_appender(&proto); + protozero::HeapBuffered<perfetto::protos::pbzero::DebugAnnotation> proto; + PerfettoProtoAppender proto_appender(proto.get()); EXPECT_TRUE(traced_value->AppendToProto(&proto_appender)); - uint32_t size = proto.Finalize(); - ProtoInputStream proto_stream(&buffer); DebugAnnotation full_proto; - EXPECT_TRUE(full_proto.ParseFromBoundedZeroCopyStream(&proto_stream, size)); + EXPECT_TRUE(full_proto.ParseFromString(proto.SerializeAsString())); EXPECT_TRUE(full_proto.has_nested_value()); return full_proto.nested_value(); diff --git a/chromium/services/tracing/public/cpp/stack_sampling/reached_code_data_source_android_unittest.cc b/chromium/services/tracing/public/cpp/stack_sampling/reached_code_data_source_android_unittest.cc index 5cf87745ba6..210e6853e6d 100644 --- a/chromium/services/tracing/public/cpp/stack_sampling/reached_code_data_source_android_unittest.cc +++ b/chromium/services/tracing/public/cpp/stack_sampling/reached_code_data_source_android_unittest.cc @@ -83,7 +83,14 @@ TEST_F(ReachedCodeDataSourceTest, ProfilerDisabled) { EXPECT_EQ(producer()->GetFinalizedPacketCount(), 0u); } -TEST_F(ReachedCodeDataSourceTest, ProfilerOutput) { +// This test crashes on the android-asan bot. +// TODO(https://crbug.com/1100216): Enable this again on the bot. +#if defined(ADDRESS_SANITIZER) +#define MAYBE_ProfilerOutput DISABLED_ProfilerOutput +#else +#define MAYBE_ProfilerOutput ProfilerOutput +#endif +TEST_F(ReachedCodeDataSourceTest, MAYBE_ProfilerOutput) { if (!base::android::IsReachedCodeProfilerSupported()) return; base::CommandLine::ForCurrentProcess()->AppendSwitch( diff --git a/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc b/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc index b99bda52188..c918f150441 100644 --- a/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc +++ b/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc @@ -35,10 +35,6 @@ #include "base/android/reached_code_profiler.h" #endif -#if defined(OS_MACOSX) -#include "base/mac/mac_util.h" -#endif - #if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \ defined(OFFICIAL_BUILD) #include <dlfcn.h> @@ -58,6 +54,9 @@ namespace tracing { namespace { +// Pointer to the main thread instance, if any. +TracingSamplerProfiler* g_main_thread_instance = nullptr; + class TracingSamplerProfilerDataSource : public PerfettoTracedProcess::DataSourceBase { public: @@ -525,7 +524,7 @@ void TracingSamplerProfiler::TracingProfileBuilder::SampleLoaderLock() { // static void TracingSamplerProfiler::MangleModuleIDIfNeeded(std::string* module_id) { -#if defined(OS_ANDROID) || defined(OS_LINUX) +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) // Linux ELF module IDs are 160bit integers, which we need to mangle // down to 128bit integers to match the id that Breakpad outputs. // Example on version '66.0.3359.170' x64: @@ -553,6 +552,11 @@ TracingSamplerProfiler::CreateOnMainThread() { InitializeLoaderLockSampling(); profiler->EnableLoaderLockSampling(); #endif + // If running in single process mode, there may be multiple "main thread" + // profilers created. In this case, we assume the first created one is the + // browser one. + if (!g_main_thread_instance) + g_main_thread_instance = profiler.get(); return profiler; } @@ -577,6 +581,12 @@ void TracingSamplerProfiler::RegisterDataSource() { TracingSamplerProfilerDataSource::Get()); } +void TracingSamplerProfiler::SetAuxUnwinderFactoryOnMainThread( + const base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>& factory) { + DCHECK(g_main_thread_instance); + g_main_thread_instance->SetAuxUnwinderFactory(factory); +} + // static void TracingSamplerProfiler::StartTracingForTesting( PerfettoProducer* producer) { @@ -615,6 +625,16 @@ TracingSamplerProfiler::TracingSamplerProfiler( TracingSamplerProfiler::~TracingSamplerProfiler() { TracingSamplerProfilerDataSource::Get()->UnregisterProfiler(this); + if (g_main_thread_instance == this) + g_main_thread_instance = nullptr; +} + +void TracingSamplerProfiler::SetAuxUnwinderFactory( + const base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>& factory) { + base::AutoLock lock(lock_); + aux_unwinder_factory_ = factory; + if (profiler_) + profiler_->AddAuxUnwinder(aux_unwinder_factory_.Run()); } void TracingSamplerProfiler::SetSampleCallbackForTesting( @@ -641,13 +661,8 @@ void TracingSamplerProfiler::StartTracing( return; #endif -#if defined(OS_MACOSX) - // TODO(https://crbug.com/1098119): Fix unwinding on OS X 10.16. The OS has - // moved all system libraries into the dyld shared cache and this seems to - // break the sampling profiler. - if (base::mac::IsOSLaterThan10_15_DontCallThis()) + if (!base::StackSamplingProfiler::IsSupported()) return; -#endif base::StackSamplingProfiler::SamplingParams params; params.samples_per_profile = std::numeric_limits<int>::max(); @@ -675,6 +690,8 @@ void TracingSamplerProfiler::StartTracing( #else // defined(OS_ANDROID) profiler_ = std::make_unique<base::StackSamplingProfiler>( sampled_thread_token_, params, std::move(profile_builder)); + if (aux_unwinder_factory_) + profiler_->AddAuxUnwinder(aux_unwinder_factory_.Run()); profiler_->Start(); #endif // defined(OS_ANDROID) } diff --git a/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h b/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h index c78647609d4..b1ab4154130 100644 --- a/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h +++ b/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h @@ -17,6 +17,7 @@ #include "base/memory/weak_ptr.h" #include "base/profiler/sampling_profiler_thread_token.h" #include "base/profiler/stack_sampling_profiler.h" +#include "base/profiler/unwinder.h" #include "base/sequence_checker.h" #include "base/threading/platform_thread.h" #include "build/build_config.h" @@ -150,6 +151,12 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler { // Registers the TracingSamplerProfiler as a Perfetto data source static void RegisterDataSource(); + // Sets a callback to create auxiliary unwinders on the main thread profiler, + // for handling additional, non-native-code unwind scenarios. + static void SetAuxUnwinderFactoryOnMainThread( + const base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>& + factory); + // For tests. static void SetupStartupTracingForTesting(); static void DeleteOnChildThreadForTesting(); @@ -160,7 +167,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler { // Returns whether of not the sampler profiling is able to unwind the stack // on this platform. constexpr static bool IsStackUnwindingSupported() { -#if defined(OS_MACOSX) || defined(OS_WIN) && defined(_WIN64) || \ +#if defined(OS_MAC) || defined(OS_WIN) && defined(_WIN64) || \ (defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \ defined(OFFICIAL_BUILD)) return true; @@ -173,6 +180,13 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler { base::SamplingProfilerThreadToken sampled_thread_token); virtual ~TracingSamplerProfiler(); + // Sets a callback to create auxiliary unwinders, for handling additional, + // non-native-code unwind scenarios. Currently used to support + // unwinding V8 JavaScript frames. + void SetAuxUnwinderFactory( + const base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>& + factory); + // The given callback will be called for every received sample, and can be // called on any thread. Must be called before tracing is started. void SetSampleCallbackForTesting( @@ -185,6 +199,9 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler { private: const base::SamplingProfilerThreadToken sampled_thread_token_; + base::RepeatingCallback<std::unique_ptr<base::Unwinder>()> + aux_unwinder_factory_; + base::Lock lock_; std::unique_ptr<base::StackSamplingProfiler> profiler_; // under |lock_| TracingProfileBuilder* profile_builder_ = nullptr; diff --git a/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc b/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc index f619fa85099..8ca971b203d 100644 --- a/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc +++ b/chromium/services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler_unittest.cc @@ -26,6 +26,10 @@ #include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h" #endif +#if defined(OS_MAC) +#include "base/mac/mac_util.h" +#endif + namespace tracing { namespace { @@ -179,9 +183,25 @@ class TestModule : public base::ModuleCache::Module { std::string id_; }; +bool ShouldSkipTestForMacOS11() { +#if defined(OS_MAC) + // The sampling profiler does not work on macOS 11 and is disabled. + // See https://crbug.com/1101399 and https://crbug.com/1098119. + // DCHECK here so that when the sampling profiler is re-enabled on macOS 11, + // these tests are also re-enabled. + if (base::mac::IsAtLeastOS11()) { + DCHECK(!base::StackSamplingProfiler::IsSupported()); + return true; + } +#endif + return false; +} + } // namespace TEST_F(TracingSampleProfilerTest, OnSampleCompleted) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; auto profiler = TracingSamplerProfiler::CreateOnMainThread(); BeginTrace(); base::RunLoop().RunUntilIdle(); @@ -192,6 +212,8 @@ TEST_F(TracingSampleProfilerTest, OnSampleCompleted) { } TEST_F(TracingSampleProfilerTest, JoinRunningTracing) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; BeginTrace(); auto profiler = TracingSamplerProfiler::CreateOnMainThread(); base::RunLoop().RunUntilIdle(); @@ -202,6 +224,8 @@ TEST_F(TracingSampleProfilerTest, JoinRunningTracing) { } TEST_F(TracingSampleProfilerTest, TestStartupTracing) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; auto profiler = TracingSamplerProfiler::CreateOnMainThread(); TracingSamplerProfiler::SetupStartupTracingForTesting(); base::RunLoop().RunUntilIdle(); @@ -236,6 +260,8 @@ TEST_F(TracingSampleProfilerTest, TestStartupTracing) { } TEST_F(TracingSampleProfilerTest, JoinStartupTracing) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; TracingSamplerProfiler::SetupStartupTracingForTesting(); base::RunLoop().RunUntilIdle(); auto profiler = TracingSamplerProfiler::CreateOnMainThread(); @@ -270,6 +296,8 @@ TEST_F(TracingSampleProfilerTest, JoinStartupTracing) { } TEST_F(TracingSampleProfilerTest, SamplingChildThread) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; base::Thread sampled_thread("sampling_profiler_test"); sampled_thread.Start(); sampled_thread.task_runner()->PostTask( @@ -288,6 +316,8 @@ TEST_F(TracingSampleProfilerTest, SamplingChildThread) { #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING) TEST_F(TracingSampleProfilerTest, SampleLoaderLockOnMainThread) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; LoaderLockEventAnalyzer event_analyzer; bool lock_held = false; @@ -316,6 +346,8 @@ TEST_F(TracingSampleProfilerTest, SampleLoaderLockOnMainThread) { } TEST_F(TracingSampleProfilerTest, SampleLoaderLockAlwaysHeld) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; LoaderLockEventAnalyzer event_analyzer; EXPECT_CALL(mock_loader_lock_sampler_, IsLoaderLockHeld()) @@ -334,6 +366,8 @@ TEST_F(TracingSampleProfilerTest, SampleLoaderLockAlwaysHeld) { } TEST_F(TracingSampleProfilerTest, SampleLoaderLockNeverHeld) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; LoaderLockEventAnalyzer event_analyzer; EXPECT_CALL(mock_loader_lock_sampler_, IsLoaderLockHeld()) @@ -351,6 +385,8 @@ TEST_F(TracingSampleProfilerTest, SampleLoaderLockNeverHeld) { } TEST_F(TracingSampleProfilerTest, SampleLoaderLockOnChildThread) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; LoaderLockEventAnalyzer event_analyzer; // Loader lock should only be sampled on main thread. @@ -373,6 +409,8 @@ TEST_F(TracingSampleProfilerTest, SampleLoaderLockOnChildThread) { } TEST_F(TracingSampleProfilerTest, SampleLoaderLockWithoutMock) { + if (ShouldSkipTestForMacOS11()) + GTEST_SKIP() << "Stack sampler is not supported on macOS 11"; // Use the real loader lock sampler. This tests that it is initialized // correctly in TracingSamplerProfiler. TracingSamplerProfiler::SetLoaderLockSamplerForTesting(nullptr); @@ -427,7 +465,7 @@ TEST_F(TracingProfileBuilderTest, InvalidModule) { base::TimeTicks()); } -#if defined(OS_ANDROID) || defined(OS_LINUX) +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) TEST_F(TracingProfileBuilderTest, MangleELFModuleID) { TestModule module; // See explanation for the module_id mangling in diff --git a/chromium/services/tracing/public/cpp/trace_event_args_allowlist.cc b/chromium/services/tracing/public/cpp/trace_event_args_allowlist.cc index 23be6e65ff1..eab6ffc9823 100644 --- a/chromium/services/tracing/public/cpp/trace_event_args_allowlist.cc +++ b/chromium/services/tracing/public/cpp/trace_event_args_allowlist.cc @@ -108,6 +108,7 @@ const AllowlistEntry kEventArgsAllowlist[] = { {nullptr, nullptr, nullptr}}; const char* kMetadataAllowlist[] = {"chrome-bitness", + "chrome-dcheck-on", "chrome-library-name", "clock-domain", "config", diff --git a/chromium/services/tracing/public/cpp/trace_startup.cc b/chromium/services/tracing/public/cpp/trace_startup.cc index 188d127cd43..7629795bd2d 100644 --- a/chromium/services/tracing/public/cpp/trace_startup.cc +++ b/chromium/services/tracing/public/cpp/trace_startup.cc @@ -38,8 +38,11 @@ void EnableStartupTracingIfNeeded() { *base::CommandLine::ForCurrentProcess(); TraceEventDataSource::GetInstance()->RegisterStartupHooks(); - // TODO(eseckler): Initialize the entire perfetto client library instead. - perfetto::internal::TrackRegistry::InitializeInstance(); + + // Initialize the Perfetto client library now that we're past the zygote's + // fork point. This is important to ensure Perfetto's track registry gets a + // unique uuid for generating track ids. + PerfettoTracedProcess::Get()->SetupClientLibrary(); // TODO(oysteine): Support startup tracing to a perfetto protobuf trace. This // should also enable TraceLog and call SetupStartupTracing(). @@ -61,6 +64,7 @@ void EnableStartupTracingIfNeeded() { PerfettoTracedProcess::Get()->producer_client(); if (startup_config->GetSessionOwner() == TraceStartupConfig::SessionOwner::kSystemTracing) { + PerfettoTracedProcess::Get()->SetupSystemTracing(); producer = PerfettoTracedProcess::Get()->system_producer(); } @@ -102,6 +106,8 @@ void InitTracingPostThreadPoolStartAndFeatureList() { // Connect to system service if available (currently a no-op except on // Posix). Has to happen on the producer's sequence, as all communication // with the system Perfetto service should occur on a single sequence. + if (!PerfettoTracedProcess::Get()->system_producer()) + PerfettoTracedProcess::Get()->SetupSystemTracing(); PerfettoTracedProcess::Get() ->GetTaskRunner() ->GetOrCreateTaskRunner() @@ -130,8 +136,10 @@ void PropagateTracingFlagsToChildProcessCmdLine(base::CommandLine* cmd_line) { // (Posix)SystemProducer doesn't currently support startup tracing, so don't // attempt to enable startup tracing in child processes if system tracing is // active. - if (PerfettoTracedProcess::Get()->system_producer()->IsTracingActive()) + if (PerfettoTracedProcess::Get()->system_producer() && + PerfettoTracedProcess::Get()->system_producer()->IsTracingActive()) { return; + } // The child process startup may race with a concurrent disabling of the // tracing session by the tracing service. To avoid being stuck in startup diff --git a/chromium/services/tracing/public/cpp/traced_process_impl.cc b/chromium/services/tracing/public/cpp/traced_process_impl.cc index 4f4668f7093..f8cadd85c7f 100644 --- a/chromium/services/tracing/public/cpp/traced_process_impl.cc +++ b/chromium/services/tracing/public/cpp/traced_process_impl.cc @@ -95,7 +95,7 @@ void TracedProcessImpl::ConnectToTracingService( // Ensure the TraceEventAgent has been created. TraceEventAgent::GetInstance(); - PerfettoTracedProcess::Get()->producer_client()->Connect( + PerfettoTracedProcess::Get()->ConnectProducer( std::move(request->perfetto_service)); } diff --git a/chromium/services/tracing/public/cpp/tracing_features.cc b/chromium/services/tracing/public/cpp/tracing_features.cc index accc5b28b6f..95980dd22f3 100644 --- a/chromium/services/tracing/public/cpp/tracing_features.cc +++ b/chromium/services/tracing/public/cpp/tracing_features.cc @@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/metrics/field_trial_params.h" #include "base/strings/string_number_conversions.h" +#include "base/tracing_buildflags.h" #include "build/build_config.h" #include "build/chromecast_buildflags.h" #include "components/tracing/common/tracing_switches.h" @@ -37,6 +38,17 @@ const base::Feature kTracingServiceInProcess { const base::Feature kEnablePerfettoSystemTracing{ "EnablePerfettoSystemTracing", base::FEATURE_DISABLED_BY_DEFAULT}; +// Controls whether trace points are implemented using Perfetto's client library +// (enabled) or legacy TraceLog (disabled). +const base::Feature kEnablePerfettoClientApiProducer { + "EnablePerfettoClientApiProducer", +#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) + base::FEATURE_ENABLED_BY_DEFAULT +#else + base::FEATURE_DISABLED_BY_DEFAULT +#endif +}; + } // namespace features namespace tracing { diff --git a/chromium/services/tracing/public/cpp/tracing_features.h b/chromium/services/tracing/public/cpp/tracing_features.h index d93d07437e3..795b20e0735 100644 --- a/chromium/services/tracing/public/cpp/tracing_features.h +++ b/chromium/services/tracing/public/cpp/tracing_features.h @@ -24,6 +24,9 @@ extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature kEnablePerfettoSystemTracing; +extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature + kEnablePerfettoClientApiProducer; + } // namespace features namespace tracing { diff --git a/chromium/services/tracing/public/mojom/BUILD.gn b/chromium/services/tracing/public/mojom/BUILD.gn index 471b688e4c1..95051f89eab 100644 --- a/chromium/services/tracing/public/mojom/BUILD.gn +++ b/chromium/services/tracing/public/mojom/BUILD.gn @@ -14,13 +14,86 @@ mojom_component("mojom") { "tracing_service.mojom", ] + public_deps = [ "//mojo/public/mojom/base" ] + if (!is_nacl && !is_ios) { enabled_features = [ "is_perfetto_supported_os" ] sources += [ "constants.mojom", "perfetto_service.mojom", ] - } - public_deps = [ "//mojo/public/mojom/base" ] + cpp_typemaps = [ + { + types = [ + { + mojom = "tracing.mojom.BufferFillPolicy" + cpp = "::perfetto::TraceConfig::BufferConfig::FillPolicy" + }, + { + mojom = "tracing.mojom.CommitDataRequest" + cpp = "::perfetto::CommitDataRequest" + }, + { + mojom = "tracing.mojom.ChunksToMove" + cpp = "::perfetto::CommitDataRequest::ChunksToMove" + }, + { + mojom = "tracing.mojom.ChunkPatch" + cpp = "::perfetto::CommitDataRequest::ChunkToPatch::Patch" + }, + { + mojom = "tracing.mojom.ChunkToPatch" + cpp = "::perfetto::CommitDataRequest::ChunkToPatch" + }, + { + mojom = "tracing.mojom.DataSourceConfig" + cpp = "::perfetto::DataSourceConfig" + }, + { + mojom = "tracing.mojom.ChromeConfig" + cpp = "::perfetto::ChromeConfig" + }, + { + mojom = "tracing.mojom.DataSourceRegistration" + cpp = "::perfetto::DataSourceDescriptor" + }, + { + mojom = "tracing.mojom.PerfettoBuiltinDataSource" + cpp = "::perfetto::TraceConfig::BuiltinDataSource" + }, + { + mojom = "tracing.mojom.IncrementalStateConfig" + cpp = "::perfetto::TraceConfig::IncrementalStateConfig" + }, + { + mojom = "tracing.mojom.TraceConfig" + cpp = "::perfetto::TraceConfig" + }, + ] + traits_headers = [ + "//third_party/perfetto/include/perfetto/ext/tracing/core/commit_data_request.h", + "//third_party/perfetto/include/perfetto/tracing/core/data_source_config.h", + "//third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h", + "//third_party/perfetto/include/perfetto/tracing/core/chrome_config.h", + "//third_party/perfetto/include/perfetto/tracing/core/trace_config.h", + ] + traits_private_headers = [ + "chrome_config_mojom_traits.h", + "commit_data_request_mojom_traits.h", + "data_source_config_mojom_traits.h", + "data_source_descriptor_mojom_traits.h", + "trace_config_mojom_traits.h", + ] + traits_sources = [ + "chrome_config_mojom_traits.cc", + "commit_data_request_mojom_traits.cc", + "data_source_config_mojom_traits.cc", + "data_source_descriptor_mojom_traits.cc", + "trace_config_mojom_traits.cc", + ] + traits_public_deps = [ "//third_party/perfetto:libperfetto" ] + }, + ] + } } diff --git a/chromium/services/tracing/public/mojom/OWNERS b/chromium/services/tracing/public/mojom/OWNERS index ae29a36aac8..1feb5149750 100644 --- a/chromium/services/tracing/public/mojom/OWNERS +++ b/chromium/services/tracing/public/mojom/OWNERS @@ -2,5 +2,3 @@ per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS per-file *_mojom_traits*.*=set noparent per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS diff --git a/chromium/services/tracing/public/mojom/chrome_config_mojom_traits.h b/chromium/services/tracing/public/mojom/chrome_config_mojom_traits.h index 1deafb3e761..87729c34656 100644 --- a/chromium/services/tracing/public/mojom/chrome_config_mojom_traits.h +++ b/chromium/services/tracing/public/mojom/chrome_config_mojom_traits.h @@ -11,7 +11,7 @@ #include <string> #include "mojo/public/cpp/bindings/struct_traits.h" -#include "services/tracing/public/mojom/perfetto_service.mojom.h" +#include "services/tracing/public/mojom/perfetto_service.mojom-shared.h" #include "third_party/perfetto/include/perfetto/tracing/core/chrome_config.h" namespace mojo { diff --git a/chromium/services/tracing/public/mojom/commit_data_request_mojom_traits.h b/chromium/services/tracing/public/mojom/commit_data_request_mojom_traits.h index e10f4857bb0..98b344fde1d 100644 --- a/chromium/services/tracing/public/mojom/commit_data_request_mojom_traits.h +++ b/chromium/services/tracing/public/mojom/commit_data_request_mojom_traits.h @@ -12,7 +12,7 @@ #include <vector> #include "mojo/public/cpp/bindings/struct_traits.h" -#include "services/tracing/public/mojom/perfetto_service.mojom.h" +#include "services/tracing/public/mojom/perfetto_service.mojom-shared.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/commit_data_request.h" namespace mojo { diff --git a/chromium/services/tracing/public/mojom/data_source_config_mojom_traits.h b/chromium/services/tracing/public/mojom/data_source_config_mojom_traits.h index e749282cfc6..44e23078f8f 100644 --- a/chromium/services/tracing/public/mojom/data_source_config_mojom_traits.h +++ b/chromium/services/tracing/public/mojom/data_source_config_mojom_traits.h @@ -11,7 +11,7 @@ #include <string> #include "mojo/public/cpp/bindings/struct_traits.h" -#include "services/tracing/public/mojom/perfetto_service.mojom.h" +#include "services/tracing/public/mojom/perfetto_service.mojom-shared.h" #include "third_party/perfetto/include/perfetto/tracing/core/chrome_config.h" #include "third_party/perfetto/include/perfetto/tracing/core/data_source_config.h" diff --git a/chromium/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h b/chromium/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h index b98db418224..4d407486ac6 100644 --- a/chromium/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h +++ b/chromium/services/tracing/public/mojom/data_source_descriptor_mojom_traits.h @@ -11,7 +11,7 @@ #include <string> #include "mojo/public/cpp/bindings/struct_traits.h" -#include "services/tracing/public/mojom/perfetto_service.mojom.h" +#include "services/tracing/public/mojom/perfetto_service.mojom-shared.h" #include "third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h" namespace mojo { diff --git a/chromium/services/tracing/public/mojom/perfetto_service.typemap b/chromium/services/tracing/public/mojom/perfetto_service.typemap deleted file mode 100644 index b55ed90693d..00000000000 --- a/chromium/services/tracing/public/mojom/perfetto_service.typemap +++ /dev/null @@ -1,43 +0,0 @@ -mojom = "//services/tracing/public/mojom/perfetto_service.mojom" -public_headers = [ - "//third_party/perfetto/include/perfetto/ext/tracing/core/commit_data_request.h", - "//third_party/perfetto/include/perfetto/tracing/core/data_source_config.h", - "//third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h", - "//third_party/perfetto/include/perfetto/tracing/core/chrome_config.h", - "//third_party/perfetto/include/perfetto/tracing/core/trace_config.h", -] -traits_headers = [ - "//services/tracing/public/mojom/commit_data_request_mojom_traits.h", - "//services/tracing/public/mojom/data_source_config_mojom_traits.h", - "//services/tracing/public/mojom/data_source_descriptor_mojom_traits.h", - "//services/tracing/public/mojom/chrome_config_mojom_traits.h", - "//services/tracing/public/mojom/trace_config_mojom_traits.h", -] -sources = [ - "//services/tracing/public/mojom/chrome_config_mojom_traits.cc", - "//services/tracing/public/mojom/chrome_config_mojom_traits.h", - "//services/tracing/public/mojom/commit_data_request_mojom_traits.cc", - "//services/tracing/public/mojom/commit_data_request_mojom_traits.h", - "//services/tracing/public/mojom/data_source_config_mojom_traits.cc", - "//services/tracing/public/mojom/data_source_config_mojom_traits.h", - "//services/tracing/public/mojom/data_source_descriptor_mojom_traits.cc", - "//services/tracing/public/mojom/data_source_descriptor_mojom_traits.h", - "//services/tracing/public/mojom/trace_config_mojom_traits.cc", - "//services/tracing/public/mojom/trace_config_mojom_traits.h", -] -public_deps = [ - "//third_party/perfetto:libperfetto", -] -type_mappings = [ - "tracing.mojom.BufferFillPolicy=::perfetto::TraceConfig::BufferConfig::FillPolicy", - "tracing.mojom.CommitDataRequest=::perfetto::CommitDataRequest", - "tracing.mojom.ChunksToMove=::perfetto::CommitDataRequest::ChunksToMove", - "tracing.mojom.ChunkPatch=::perfetto::CommitDataRequest::ChunkToPatch::Patch", - "tracing.mojom.ChunkToPatch=::perfetto::CommitDataRequest::ChunkToPatch", - "tracing.mojom.DataSourceConfig=::perfetto::DataSourceConfig", - "tracing.mojom.ChromeConfig=::perfetto::ChromeConfig", - "tracing.mojom.DataSourceRegistration=::perfetto::DataSourceDescriptor", - "tracing.mojom.PerfettoBuiltinDataSource=::perfetto::TraceConfig::BuiltinDataSource", - "tracing.mojom.IncrementalStateConfig=::perfetto::TraceConfig::IncrementalStateConfig", - "tracing.mojom.TraceConfig=::perfetto::TraceConfig", -] diff --git a/chromium/services/tracing/public/mojom/trace_config_mojom_traits.h b/chromium/services/tracing/public/mojom/trace_config_mojom_traits.h index 05b2aaea494..87d73c76921 100644 --- a/chromium/services/tracing/public/mojom/trace_config_mojom_traits.h +++ b/chromium/services/tracing/public/mojom/trace_config_mojom_traits.h @@ -13,7 +13,7 @@ #include "mojo/public/cpp/bindings/enum_traits.h" #include "mojo/public/cpp/bindings/struct_traits.h" -#include "services/tracing/public/mojom/perfetto_service.mojom.h" +#include "services/tracing/public/mojom/perfetto_service.mojom-shared.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" namespace mojo { diff --git a/chromium/services/tracing/public/mojom/typemaps.gni b/chromium/services/tracing/public/mojom/typemaps.gni deleted file mode 100644 index 1544fb1844f..00000000000 --- a/chromium/services/tracing/public/mojom/typemaps.gni +++ /dev/null @@ -1 +0,0 @@ -typemaps = [ "//services/tracing/public/mojom/perfetto_service.typemap" ] diff --git a/chromium/services/tracing/tracing_service.cc b/chromium/services/tracing/tracing_service.cc index 8001350a646..fa0bc1fbec4 100644 --- a/chromium/services/tracing/tracing_service.cc +++ b/chromium/services/tracing/tracing_service.cc @@ -15,21 +15,28 @@ namespace tracing { namespace { +void OnProcessConnectFailed(PerfettoService* perfetto_service, uint32_t pid) { + perfetto_service->RemoveActiveServicePidIfNoActiveConnections(pid); +} + void OnProcessConnected( + PerfettoService* perfetto_service, mojo::Remote<mojom::TracedProcess> traced_process, uint32_t pid, mojo::PendingReceiver<mojom::PerfettoService> service_receiver) { - PerfettoService::GetInstance()->BindReceiver(std::move(service_receiver), - pid); + perfetto_service->BindReceiver(std::move(service_receiver), pid); } } // namespace -TracingService::TracingService() = default; +TracingService::TracingService(PerfettoService* perfetto_service) + : perfetto_service_(perfetto_service ? perfetto_service + : PerfettoService::GetInstance()) {} TracingService::TracingService( mojo::PendingReceiver<mojom::TracingService> receiver) - : receiver_(this, std::move(receiver)) {} + : receiver_(this, std::move(receiver)), + perfetto_service_(PerfettoService::GetInstance()) {} TracingService::~TracingService() = default; @@ -37,28 +44,38 @@ void TracingService::Initialize(std::vector<mojom::ClientInfoPtr> clients) { for (auto& client : clients) { AddClient(std::move(client)); } - PerfettoService::GetInstance()->SetActiveServicePidsInitialized(); + perfetto_service_->SetActiveServicePidsInitialized(); } void TracingService::AddClient(mojom::ClientInfoPtr client) { - PerfettoService::GetInstance()->AddActiveServicePid(client->pid); - mojo::Remote<mojom::TracedProcess> process(std::move(client->process)); + + perfetto_service_->AddActiveServicePid(client->pid); + + // If the remote traced process goes away before ConnectToTracingService + // responds, the PID should be removed from the list of active service PID. + // Note that the perfetto service will start monitoring disconnects after the + // service receiver is bound to it in OnProcessConnected(). + process.set_disconnect_handler( + base::BindOnce(&OnProcessConnectFailed, + base::Unretained(perfetto_service_), client->pid)); + auto new_connection_request = mojom::ConnectToTracingRequest::New(); auto service_receiver = new_connection_request->perfetto_service.InitWithNewPipeAndPassReceiver(); + mojom::TracedProcess* raw_process = process.get(); raw_process->ConnectToTracingService( std::move(new_connection_request), - base::BindOnce(&OnProcessConnected, std::move(process), client->pid, + base::BindOnce(&OnProcessConnected, base::Unretained(perfetto_service_), + std::move(process), client->pid, std::move(service_receiver))); } #if !defined(OS_NACL) && !defined(OS_IOS) void TracingService::BindConsumerHost( mojo::PendingReceiver<mojom::ConsumerHost> receiver) { - ConsumerHost::BindConsumerReceiver(PerfettoService::GetInstance(), - std::move(receiver)); + ConsumerHost::BindConsumerReceiver(perfetto_service_, std::move(receiver)); } #endif diff --git a/chromium/services/tracing/tracing_service.h b/chromium/services/tracing/tracing_service.h index 0b29b2d6499..9651aec1db0 100644 --- a/chromium/services/tracing/tracing_service.h +++ b/chromium/services/tracing/tracing_service.h @@ -12,9 +12,11 @@ namespace tracing { +class PerfettoService; + class TracingService : public mojom::TracingService { public: - TracingService(); + explicit TracingService(PerfettoService* = nullptr); explicit TracingService( mojo::PendingReceiver<mojom::TracingService> receiver); TracingService(const TracingService&) = delete; @@ -31,6 +33,7 @@ class TracingService : public mojom::TracingService { private: mojo::Receiver<mojom::TracingService> receiver_{this}; + PerfettoService* perfetto_service_; }; } // namespace tracing diff --git a/chromium/services/tracing/tracing_service_unittest.cc b/chromium/services/tracing/tracing_service_unittest.cc index e33db3956bb..6e5cb97b84b 100644 --- a/chromium/services/tracing/tracing_service_unittest.cc +++ b/chromium/services/tracing/tracing_service_unittest.cc @@ -6,31 +6,130 @@ #include <utility> #include "base/bind.h" +#include "base/json/json_reader.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/test/bind_test_util.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "base/test/test_simple_task_runner.h" +#include "base/threading/thread.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote.h" +#include "services/tracing/perfetto/test_utils.h" #include "services/tracing/public/cpp/perfetto/perfetto_config.h" +#include "services/tracing/public/cpp/perfetto/perfetto_platform.h" +#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" +#include "services/tracing/public/cpp/perfetto/trace_packet_tokenizer.h" +#include "services/tracing/public/cpp/traced_process_impl.h" +#include "services/tracing/public/cpp/tracing_features.h" #include "services/tracing/public/mojom/perfetto_service.mojom.h" +#include "services/tracing/public/mojom/traced_process.mojom.h" #include "services/tracing/public/mojom/tracing_service.mojom.h" #include "services/tracing/tracing_service.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h" +#include "third_party/perfetto/include/perfetto/tracing/data_source.h" +#include "third_party/perfetto/include/perfetto/tracing/tracing.h" +#include "third_party/perfetto/protos/perfetto/common/trace_stats.pb.h" +#include "third_party/perfetto/protos/perfetto/trace/test_event.pbzero.h" +#include "third_party/perfetto/protos/perfetto/trace/trace.pb.h" +#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pb.h" +#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h" namespace tracing { +namespace { + +RebindableTaskRunner* GetPerfettoTaskRunner() { + static base::NoDestructor<RebindableTaskRunner> task_runner; + return task_runner.get(); +} + +} // namespace class TracingServiceTest : public testing::Test { public: - TracingServiceTest() = default; + TracingServiceTest() : service_(&perfetto_service_) { + // Since Perfetto's platform backend can only be initialized once in a + // process, we give it a task runner that can outlive the per-test task + // environment. + auto* perfetto_task_runner = GetPerfettoTaskRunner(); + auto* perfetto_platform = + PerfettoTracedProcess::Get()->perfetto_platform_for_testing(); + if (!perfetto_platform->did_start_task_runner()) + perfetto_platform->StartTaskRunner(perfetto_task_runner); + perfetto_task_runner->set_task_runner(base::ThreadTaskRunnerHandle::Get()); + + // Also tell PerfettoTracedProcess to use the current task environment. + PerfettoTracedProcess::ResetTaskRunnerForTesting( + base::ThreadTaskRunnerHandle::Get()); + PerfettoTracedProcess::Get()->SetupClientLibrary(); + perfetto_service()->SetActiveServicePidsInitialized(); + } ~TracingServiceTest() override = default; protected: + PerfettoService* perfetto_service() { return &perfetto_service_; } mojom::TracingService* service() { return &service_; } + void EnableClientApiConsumer() { + // Tell PerfettoTracedProcess how to connect to the service. This enables + // the consumer part of the client API. + static mojom::TracingService* s_service; + s_service = service(); + auto factory = []() -> mojom::TracingService& { return *s_service; }; + PerfettoTracedProcess::Get()->SetConsumerConnectionFactory( + factory, base::SequencedTaskRunnerHandle::Get()); + } + + void EnableClientApiProducer() { + // Connect the producer part of the client API. AddClient() will end up + // calling TracedProcessImpl::ConnectToTracingService(), which will in turn + // route the PerfettoService interface to the client API backend. + mojo::PendingRemote<tracing::mojom::TracedProcess> traced_process_remote; + traced_process_receiver_ = + std::make_unique<mojo::Receiver<tracing::mojom::TracedProcess>>( + tracing::TracedProcessImpl::GetInstance()); + traced_process_receiver_->Bind( + traced_process_remote.InitWithNewPipeAndPassReceiver()); + auto client_info = mojom::ClientInfo::New(base::GetCurrentProcId(), + std::move(traced_process_remote)); + service()->AddClient(std::move(client_info)); + perfetto_service()->SetActiveServicePidsInitialized(); + } + + size_t ReadAndCountTestPackets(perfetto::TracingSession& session) { + size_t test_packet_count = 0; + base::RunLoop wait_for_data_loop; + session.ReadTrace( + [&wait_for_data_loop, &test_packet_count]( + perfetto::TracingSession::ReadTraceCallbackArgs args) { + if (args.size) { + perfetto::protos::Trace trace; + EXPECT_TRUE(trace.ParseFromArray(args.data, args.size)); + for (const auto& packet : trace.packet()) { + if (packet.has_for_testing()) { + EXPECT_EQ(kPerfettoTestString, packet.for_testing().str()); + test_packet_count++; + } + } + } + if (!args.has_more) + wait_for_data_loop.Quit(); + }); + wait_for_data_loop.Run(); + return test_packet_count; + } + private: base::test::TaskEnvironment task_environment_; + PerfettoService perfetto_service_; TracingService service_; + std::unique_ptr<mojo::Receiver<tracing::mojom::TracedProcess>> + traced_process_receiver_; + DISALLOW_COPY_AND_ASSIGN(TracingServiceTest); }; @@ -55,11 +154,20 @@ class TestTracingClient : public mojom::TracingSessionClient { std::move(on_tracing_enabled))); } + void StopTracing(base::OnceClosure on_tracing_stopped) { + tracing_disabled_callback_ = std::move(on_tracing_stopped); + tracing_session_host_->DisableTracing(); + } + // tracing::mojom::TracingSessionClient implementation: void OnTracingEnabled() override {} - void OnTracingDisabled() override {} + void OnTracingDisabled() override { + std::move(tracing_disabled_callback_).Run(); + } private: + base::OnceClosure tracing_disabled_callback_; + mojo::Remote<mojom::ConsumerHost> consumer_host_; mojo::Remote<mojom::TracingSessionHost> tracing_session_host_; mojo::Receiver<mojom::TracingSessionClient> receiver_{this}; @@ -71,6 +179,205 @@ TEST_F(TracingServiceTest, TracingServiceInstantiate) { base::RunLoop tracing_started; tracing_client.StartTracing(service(), tracing_started.QuitClosure()); tracing_started.Run(); + + base::RunLoop tracing_stopped; + tracing_client.StopTracing(tracing_stopped.QuitClosure()); + tracing_stopped.Run(); +} + +TEST_F(TracingServiceTest, PerfettoClientConsumer) { + // Set up API bindings. + EnableClientApiConsumer(); + + // Register a mock producer with an in-process Perfetto service. + auto pid = 123; + size_t kNumPackets = 10; + base::RunLoop wait_for_start; + base::RunLoop wait_for_registration; + std::unique_ptr<MockProducer> producer = std::make_unique<MockProducer>( + std::string("org.chromium-") + base::NumberToString(pid), + "com.example.mock_data_source", perfetto_service(), + wait_for_registration.QuitClosure(), wait_for_start.QuitClosure(), + kNumPackets); + wait_for_registration.Run(); + + // Start a tracing session using the client API. + auto session = perfetto::Tracing::NewTrace(); + perfetto::TraceConfig perfetto_config; + perfetto_config.add_buffers()->set_size_kb(1024); + auto* ds_cfg = perfetto_config.add_data_sources()->mutable_config(); + ds_cfg->set_name("com.example.mock_data_source"); + session->Setup(perfetto_config); + session->Start(); + wait_for_start.Run(); + + // Stop the session and wait for it to stop. Note that we can't use the + // blocking API here because the service runs on the current sequence. + base::RunLoop wait_for_stop_loop; + session->SetOnStopCallback( + [&wait_for_stop_loop] { wait_for_stop_loop.Quit(); }); + session->Stop(); + wait_for_stop_loop.Run(); + + // Verify tracing session statistics. + base::RunLoop wait_for_stats_loop; + perfetto::protos::TraceStats stats; + auto stats_callback = + [&wait_for_stats_loop, + &stats](perfetto::TracingSession::GetTraceStatsCallbackArgs args) { + EXPECT_TRUE(args.success); + EXPECT_TRUE(stats.ParseFromArray(args.trace_stats_data.data(), + args.trace_stats_data.size())); + wait_for_stats_loop.Quit(); + }; + session->GetTraceStats(std::move(stats_callback)); + wait_for_stats_loop.Run(); + EXPECT_EQ(1024u * 1024u, stats.buffer_stats(0).buffer_size()); + EXPECT_GT(stats.buffer_stats(0).bytes_written(), 0u); + EXPECT_EQ(0u, stats.buffer_stats(0).trace_writer_packet_loss()); + + // Read and verify the data. + EXPECT_EQ(kNumPackets, ReadAndCountTestPackets(*session)); +} + +TEST_F(TracingServiceTest, PerfettoClientConsumerLegacyJson) { + // Set up API bindings. + EnableClientApiConsumer(); + + // Start a tracing session with legacy JSON exporting. + auto session = perfetto::Tracing::NewTrace(); + perfetto::TraceConfig perfetto_config = GetDefaultPerfettoConfig( + base::trace_event::TraceConfig(), /*privacy_filtering_enabled=*/false, + /*convert_to_legacy_json=*/true); + session->Setup(perfetto_config); + session->Start(); + + // Stop the session and wait for it to stop. Note that we can't use the + // blocking API here because the service runs on the current sequence. + base::RunLoop wait_for_stop_loop; + session->SetOnStopCallback( + [&wait_for_stop_loop] { wait_for_stop_loop.Quit(); }); + session->Stop(); + wait_for_stop_loop.Run(); + + // Read and verify the JSON data. + base::RunLoop wait_for_data_loop; + TracePacketTokenizer tokenizer; + std::string json; + session->ReadTrace([&wait_for_data_loop, &tokenizer, &json]( + perfetto::TracingSession::ReadTraceCallbackArgs args) { + if (args.size) { + auto packets = tokenizer.Parse( + reinterpret_cast<const uint8_t*>(args.data), args.size); + for (const auto& packet : packets) { + for (const auto& slice : packet.slices()) { + json += std::string(reinterpret_cast<const char*>(slice.start), + slice.size); + } + } + } + if (!args.has_more) + wait_for_data_loop.Quit(); + }); + wait_for_data_loop.Run(); + DCHECK(!tokenizer.has_more()); + + auto result = base::DictionaryValue::From( + base::Value::ToUniquePtrValue(*base::JSONReader::Read(json))); + EXPECT_TRUE(result->HasKey("traceEvents")); +} + +class CustomDataSource : public perfetto::DataSource<CustomDataSource> { + public: + struct Events { + base::RunLoop wait_for_setup_loop; + base::RunLoop wait_for_start_loop; + base::RunLoop wait_for_stop_loop; + }; + + static void set_events(Events* events) { events_ = events; } + + void OnSetup(const SetupArgs&) override { + events_->wait_for_setup_loop.Quit(); + } + void OnStart(const StartArgs&) override { + events_->wait_for_start_loop.Quit(); + } + void OnStop(const StopArgs&) override { events_->wait_for_stop_loop.Quit(); } + + private: + static Events* events_; +}; + +CustomDataSource::Events* CustomDataSource::events_; + +TEST_F(TracingServiceTest, PerfettoClientProducer) { + // Use the client API as the producer for this process instead of + // ProducerClient. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kEnablePerfettoClientApiProducer); + + // Set up API bindings. + EnableClientApiConsumer(); + EnableClientApiProducer(); + + perfetto::DataSourceDescriptor dsd; + dsd.set_name("com.example.custom_data_source"); + CustomDataSource::Events ds_events; + CustomDataSource::set_events(&ds_events); + CustomDataSource::Register(dsd); + + // Start a tracing session using the client API. + auto session = perfetto::Tracing::NewTrace(); + perfetto::TraceConfig perfetto_config; + perfetto_config.add_buffers()->set_size_kb(1024); + auto* ds_cfg = perfetto_config.add_data_sources()->mutable_config(); + ds_cfg->set_name("com.example.custom_data_source"); + session->Setup(perfetto_config); + session->Start(); + + ds_events.wait_for_setup_loop.Run(); + ds_events.wait_for_start_loop.Run(); + + // Write more data to check the commit flow works too. + size_t kNumPackets = 1000; + CustomDataSource::Trace([kNumPackets](CustomDataSource::TraceContext ctx) { + for (size_t i = 0; i < kNumPackets / 2; i++) { + ctx.NewTracePacket()->set_for_testing()->set_str( + tracing::kPerfettoTestString); + } + ctx.Flush(); + }); + + // Write half of the data from another thread to check TLS is hooked up + // properly. + base::Thread thread("TestThread"); + thread.Start(); + thread.task_runner()->PostTask( + FROM_HERE, base::BindLambdaForTesting([kNumPackets] { + CustomDataSource::Trace( + [kNumPackets](CustomDataSource::TraceContext ctx) { + for (size_t i = 0; i < kNumPackets / 2; i++) { + ctx.NewTracePacket()->set_for_testing()->set_str( + tracing::kPerfettoTestString); + } + ctx.Flush(); + }); + })); + thread.Stop(); + + // Stop the session and wait for it to stop. Note that we can't use the + // blocking variants here because the service runs on the current sequence. + base::RunLoop wait_for_stop_loop; + session->SetOnStopCallback( + [&wait_for_stop_loop] { wait_for_stop_loop.Quit(); }); + session->Stop(); + ds_events.wait_for_stop_loop.Run(); + wait_for_stop_loop.Run(); + + // Read and verify the data. + EXPECT_EQ(kNumPackets, ReadAndCountTestPackets(*session)); } } // namespace tracing diff --git a/chromium/services/video_capture/public/uma/video_capture_service_event.cc b/chromium/services/video_capture/public/uma/video_capture_service_event.cc index 57a93e292fe..4d3096d1bbb 100644 --- a/chromium/services/video_capture/public/uma/video_capture_service_event.cc +++ b/chromium/services/video_capture/public/uma/video_capture_service_event.cc @@ -57,7 +57,7 @@ void LogDurationUntilReconnectAfterCapture(base::TimeDelta duration) { DVLOG(4) << "Logged DurationUntilReconnectAfterCapture"; } -#if defined(OS_MACOSX) +#if defined(OS_MAC) void LogMacbookRetryGetDeviceInfosEvent(MacbookRetryGetDeviceInfosEvent event) { UMA_HISTOGRAM_ENUMERATION( "Media.VideoCapture.MacBook.RetryGetDeviceInfosEvent", event, diff --git a/chromium/services/video_capture/public/uma/video_capture_service_event.h b/chromium/services/video_capture/public/uma/video_capture_service_event.h index 554dd4f8ec5..5683fd808bd 100644 --- a/chromium/services/video_capture/public/uma/video_capture_service_event.h +++ b/chromium/services/video_capture/public/uma/video_capture_service_event.h @@ -29,7 +29,7 @@ enum VideoCaptureServiceEvent { NUM_VIDEO_CAPTURE_SERVICE_EVENT }; -#if defined(OS_MACOSX) +#if defined(OS_MAC) enum MacbookRetryGetDeviceInfosEvent { PROVIDER_RECEIVED_ZERO_INFOS_STOPPING_SERVICE = 0, PROVIDER_SERVICE_STOPPED_ISSUING_RETRY = 1, @@ -61,7 +61,7 @@ void LogDurationFromLastConnectToConnectionLost(base::TimeDelta duration); void LogDurationUntilReconnectAfterEnumerationOnly(base::TimeDelta duration); void LogDurationUntilReconnectAfterCapture(base::TimeDelta duration); -#if defined(OS_MACOSX) +#if defined(OS_MAC) void LogMacbookRetryGetDeviceInfosEvent(MacbookRetryGetDeviceInfosEvent event); #endif diff --git a/chromium/services/video_capture/texture_virtual_device_mojo_adapter.cc b/chromium/services/video_capture/texture_virtual_device_mojo_adapter.cc index e04babecbb9..fed38116008 100644 --- a/chromium/services/video_capture/texture_virtual_device_mojo_adapter.cc +++ b/chromium/services/video_capture/texture_virtual_device_mojo_adapter.cc @@ -9,7 +9,6 @@ #include "base/memory/ptr_util.h" #include "media/base/bind_to_current_loop.h" #include "mojo/public/cpp/bindings/callback_helpers.h" -#include "mojo/public/cpp/bindings/strong_binding.h" #include "services/video_capture/public/mojom/constants.mojom.h" #include "services/video_capture/public/mojom/scoped_access_permission.mojom.h" diff --git a/chromium/services/video_capture/video_capture_service_impl.cc b/chromium/services/video_capture/video_capture_service_impl.cc index 0d5b3a1468c..27844d6f531 100644 --- a/chromium/services/video_capture/video_capture_service_impl.cc +++ b/chromium/services/video_capture/video_capture_service_impl.cc @@ -27,23 +27,12 @@ #include "services/video_capture/virtual_device_enabled_device_factory.h" #include "services/viz/public/cpp/gpu/gpu.h" -#if defined(OS_MACOSX) +#if defined(OS_MAC) #include "media/capture/video/mac/video_capture_device_factory_mac.h" #endif namespace video_capture { -namespace { - -// Used to assess the impact of running the video capture service at background -// priority. If the experiment confirms that running the video capture service -// at background priority causes jank, change the code to use a foreground -// priority by default. See https://crbug.com/1066137. -const base::Feature kForegroundVideoCaptureService{ - "ForegroundVideoCaptureService", base::FEATURE_DISABLED_BY_DEFAULT}; - -} // namespace - // Intended usage of this class is to instantiate on any sequence, and then // operate and release the instance on the task runner exposed via // GetTaskRunner() via WeakPtrs provided via GetWeakPtr(). To this end, @@ -52,13 +41,8 @@ const base::Feature kForegroundVideoCaptureService{ class VideoCaptureServiceImpl::GpuDependenciesContext { public: GpuDependenciesContext() { - const base::TaskPriority priority = - base::FeatureList::IsEnabled(kForegroundVideoCaptureService) - ? base::TaskPriority::USER_BLOCKING - : base::TaskPriority::BEST_EFFORT; - gpu_io_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner( - {priority, base::MayBlock()}); + {base::TaskPriority::USER_BLOCKING, base::MayBlock()}); } ~GpuDependenciesContext() { @@ -158,8 +142,8 @@ void VideoCaptureServiceImpl::ConnectToVideoSourceProvider( } void VideoCaptureServiceImpl::SetRetryCount(int32_t count) { -#if defined(OS_MACOSX) - media::VideoCaptureDeviceFactoryMac::SetGetDeviceDescriptorsRetryCount(count); +#if defined(OS_MAC) + media::VideoCaptureDeviceFactoryMac::SetGetDevicesInfoRetryCount(count); #endif } diff --git a/chromium/services/viz/PRESUBMIT.py b/chromium/services/viz/PRESUBMIT.py index babbf3fe2a0..2d7842caaef 100644 --- a/chromium/services/viz/PRESUBMIT.py +++ b/chromium/services/viz/PRESUBMIT.py @@ -12,5 +12,5 @@ def CheckChangeOnUpload(input_api, output_api): 'components', 'viz')] import presubmit_checks as ps - white_list=(r'^services[\\/]viz[\\/].*\.(cc|h)$',) - return ps.RunAllChecks(input_api, output_api, white_list) + allowlist=(r'^services[\\/]viz[\\/].*\.(cc|h)$',) + return ps.RunAllChecks(input_api, output_api, allowlist) diff --git a/chromium/services/viz/privileged/mojom/compositing/BUILD.gn b/chromium/services/viz/privileged/mojom/compositing/BUILD.gn index 53af38d06f5..84ad1023325 100644 --- a/chromium/services/viz/privileged/mojom/compositing/BUILD.gn +++ b/chromium/services/viz/privileged/mojom/compositing/BUILD.gn @@ -29,6 +29,8 @@ mojom("compositing") { "//ui/latency/mojom", ] + traits_public_deps = [ "//ui/base:features" ] + enabled_features = [] if (use_ozone) { enabled_features += [ "use_ozone" ] @@ -44,10 +46,14 @@ mojom("compositing") { mojom = "viz.mojom.RendererSettings" cpp = "::viz::RendererSettings" }, + { + mojom = "viz.mojom.DebugRendererSettings" + cpp = "::viz::DebugRendererSettings" + }, ] traits_headers = [ "//services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h" ] traits_sources = [ "//services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.cc" ] - traits_public_deps = [ + traits_public_deps += [ "//cc", "//ui/gfx/geometry/mojom", ] diff --git a/chromium/services/viz/privileged/mojom/compositing/DEPS b/chromium/services/viz/privileged/mojom/compositing/DEPS index b81a4a4e7bc..131cc87bdec 100644 --- a/chromium/services/viz/privileged/mojom/compositing/DEPS +++ b/chromium/services/viz/privileged/mojom/compositing/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+ui/gfx/geometry/mojom/geometry_mojom_traits.h", "+ui/gfx/mojom/color_space_mojom_traits.h", + "+ui/base/ui_base_features.h", ] diff --git a/chromium/services/viz/privileged/mojom/compositing/display_private.mojom b/chromium/services/viz/privileged/mojom/compositing/display_private.mojom index d88fab3b7d0..0c9686f3c80 100644 --- a/chromium/services/viz/privileged/mojom/compositing/display_private.mojom +++ b/chromium/services/viz/privileged/mojom/compositing/display_private.mojom @@ -14,6 +14,7 @@ import "ui/gfx/geometry/mojom/geometry.mojom"; import "ui/latency/mojom/latency_info.mojom"; import "services/viz/privileged/mojom/compositing/layered_window_updater.mojom"; import "services/viz/privileged/mojom/compositing/vsync_parameter_observer.mojom"; +import "services/viz/public/mojom/compositing/delegated_ink_point.mojom"; // The DisplayPrivate is used by privileged clients to talk to Display. interface DisplayPrivate { @@ -71,6 +72,11 @@ interface DisplayPrivate { // support multiple observers. AddVSyncParameterObserver( pending_remote<VSyncParameterObserver> observer); + + // Bind an interface between browser process and viz for ink points to be + // sent to viz and stored, to be used when drawing a delegated ink trail. + SetDelegatedInkPointRenderer( + pending_receiver<DelegatedInkPointRenderer> receiver); }; interface DisplayClient { diff --git a/chromium/services/viz/privileged/mojom/compositing/frame_sink_manager.mojom b/chromium/services/viz/privileged/mojom/compositing/frame_sink_manager.mojom index c7a8c1e6f02..195bd5568af 100644 --- a/chromium/services/viz/privileged/mojom/compositing/frame_sink_manager.mojom +++ b/chromium/services/viz/privileged/mojom/compositing/frame_sink_manager.mojom @@ -35,6 +35,9 @@ struct RootCompositorFrameSinkParams { [EnableIf=is_android] float refresh_rate; + [EnableIf=is_win] + bool set_present_duration_allowed = false; + pending_associated_receiver<CompositorFrameSink> compositor_frame_sink; pending_remote<CompositorFrameSinkClient> compositor_frame_sink_client; @@ -122,6 +125,19 @@ interface FrameSinkManager { // Marks the given SurfaceIds for destruction. EvictSurfaces(array<SurfaceId> surface_ids); + // Starts throttling the frame sinks specified by |frame_sink_ids| and all + // their descendant sinks to send BeginFrames at an interval of |interval|. + // |interval| should be greater than zero. Calling this function before + // calling EndThrottling() to end a previous throttling operation will + // automatically end the previous operation before applying the current + // throttling operation. + StartThrottling( + array<FrameSinkId> frame_sink_ids, + mojo_base.mojom.TimeDelta interval); + + // Ends all previously throttled frame sinks. + EndThrottling(); + // Takes a snapshot of |surface_id| or a newer surface with the same // FrameSinkId. The request will be queued up until such surface exists and is // reachable from the root surface. @@ -141,6 +157,10 @@ interface FrameSinkManager { [Sync] EvictBackBuffer(uint32 cache_id) => (); + + // This allows dynamic manipulation of the viz debug options stored in + // |debug_settings| (show_overdraw_feedback, etc.). + UpdateDebugRendererSettings(DebugRendererSettings debug_settings); }; // The FrameSinkManagerClient interface is implemented by the Display diff --git a/chromium/services/viz/privileged/mojom/compositing/renderer_settings.mojom b/chromium/services/viz/privileged/mojom/compositing/renderer_settings.mojom index 3974ab6072b..36a5d07d757 100644 --- a/chromium/services/viz/privileged/mojom/compositing/renderer_settings.mojom +++ b/chromium/services/viz/privileged/mojom/compositing/renderer_settings.mojom @@ -12,20 +12,16 @@ struct RendererSettings { bool allow_antialiasing; bool force_antialiasing; bool force_blending_with_shaders; - bool tint_gl_composited_content; int32 highp_threshold_min; bool partial_swap_enabled; bool release_overlay_resources_after_gpu_query; bool should_clear_root_render_pass; - bool show_overdraw_feedback; - bool show_aggregated_damage; int32 slow_down_compositing_scale_factor; bool use_skia_renderer; bool record_sk_picture; bool allow_overlays; bool auto_resize_output_surface; bool requires_alpha_channel; - bool show_dc_layer_debug_borders; [EnableIf=is_android] gfx.mojom.Size initial_screen_size; @@ -36,3 +32,10 @@ struct RendererSettings { [EnableIf=use_ozone] array<OverlayStrategy> overlay_strategies; }; + +struct DebugRendererSettings { + bool tint_composited_content; + bool show_overdraw_feedback; + bool show_dc_layer_debug_borders; + bool show_aggregated_damage; +}; diff --git a/chromium/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.cc b/chromium/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.cc index c5430439c18..eed93b40ba8 100644 --- a/chromium/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.cc +++ b/chromium/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.cc @@ -5,6 +5,7 @@ #include "services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h" #include "services/viz/public/cpp/compositing/resource_settings_mojom_traits.h" +#include "ui/base/ui_base_features.h" #if defined(OS_ANDROID) #include "ui/gfx/mojom/color_space_mojom_traits.h" @@ -13,6 +14,18 @@ namespace mojo { // static +bool StructTraits<viz::mojom::DebugRendererSettingsDataView, + viz::DebugRendererSettings>:: + Read(viz::mojom::DebugRendererSettingsDataView data, + viz::DebugRendererSettings* out) { + out->tint_composited_content = data.tint_composited_content(); + out->show_overdraw_feedback = data.show_overdraw_feedback(); + out->show_dc_layer_debug_borders = data.show_dc_layer_debug_borders(); + out->show_aggregated_damage = data.show_aggregated_damage(); + return true; +} + +// static bool StructTraits<viz::mojom::RendererSettingsDataView, viz::RendererSettings>:: Read(viz::mojom::RendererSettingsDataView data, viz::RendererSettings* out) { @@ -23,9 +36,6 @@ bool StructTraits<viz::mojom::RendererSettingsDataView, viz::RendererSettings>:: out->should_clear_root_render_pass = data.should_clear_root_render_pass(); out->release_overlay_resources_after_gpu_query = data.release_overlay_resources_after_gpu_query(); - out->tint_gl_composited_content = data.tint_gl_composited_content(); - out->show_overdraw_feedback = data.show_overdraw_feedback(); - out->show_aggregated_damage = data.show_aggregated_damage(); out->highp_threshold_min = data.highp_threshold_min(); out->slow_down_compositing_scale_factor = data.slow_down_compositing_scale_factor(); @@ -34,7 +44,6 @@ bool StructTraits<viz::mojom::RendererSettingsDataView, viz::RendererSettings>:: out->allow_overlays = data.allow_overlays(); out->auto_resize_output_surface = data.auto_resize_output_surface(); out->requires_alpha_channel = data.requires_alpha_channel(); - out->show_dc_layer_debug_borders = data.show_dc_layer_debug_borders(); #if defined(OS_ANDROID) if (!data.ReadInitialScreenSize(&out->initial_screen_size)) @@ -45,7 +54,8 @@ bool StructTraits<viz::mojom::RendererSettingsDataView, viz::RendererSettings>:: #endif #if defined(USE_OZONE) - if (!data.ReadOverlayStrategies(&out->overlay_strategies)) + if (features::IsUsingOzonePlatform() && + !data.ReadOverlayStrategies(&out->overlay_strategies)) return false; #endif diff --git a/chromium/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h b/chromium/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h index 5cb17bdd2ec..09efec4657f 100644 --- a/chromium/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h +++ b/chromium/services/viz/privileged/mojom/compositing/renderer_settings_mojom_traits.h @@ -18,6 +18,30 @@ namespace mojo { template <> +struct StructTraits<viz::mojom::DebugRendererSettingsDataView, + viz::DebugRendererSettings> { + static bool tint_composited_content(const viz::DebugRendererSettings& input) { + return input.tint_composited_content; + } + + static bool show_overdraw_feedback(const viz::DebugRendererSettings& input) { + return input.show_overdraw_feedback; + } + + static bool show_dc_layer_debug_borders( + const viz::DebugRendererSettings& input) { + return input.show_dc_layer_debug_borders; + } + + static bool show_aggregated_damage(const viz::DebugRendererSettings& input) { + return input.show_aggregated_damage; + } + + static bool Read(viz::mojom::DebugRendererSettingsDataView data, + viz::DebugRendererSettings* out); +}; + +template <> struct StructTraits<viz::mojom::RendererSettingsDataView, viz::RendererSettings> { static bool allow_antialiasing(const viz::RendererSettings& input) { @@ -46,18 +70,6 @@ struct StructTraits<viz::mojom::RendererSettingsDataView, return input.release_overlay_resources_after_gpu_query; } - static bool tint_gl_composited_content(const viz::RendererSettings& input) { - return input.tint_gl_composited_content; - } - - static bool show_overdraw_feedback(const viz::RendererSettings& input) { - return input.show_overdraw_feedback; - } - - static bool show_aggregated_damage(const viz::RendererSettings& input) { - return input.show_aggregated_damage; - } - static int highp_threshold_min(const viz::RendererSettings& input) { return input.highp_threshold_min; } @@ -87,10 +99,6 @@ struct StructTraits<viz::mojom::RendererSettingsDataView, return input.requires_alpha_channel; } - static bool show_dc_layer_debug_borders(const viz::RendererSettings& input) { - return input.show_dc_layer_debug_borders; - } - #if defined(OS_ANDROID) static gfx::Size initial_screen_size(const viz::RendererSettings& input) { return input.initial_screen_size; diff --git a/chromium/services/viz/privileged/mojom/mojom_traits_unittest.cc b/chromium/services/viz/privileged/mojom/mojom_traits_unittest.cc index d1625920954..9790ccbaa01 100644 --- a/chromium/services/viz/privileged/mojom/mojom_traits_unittest.cc +++ b/chromium/services/viz/privileged/mojom/mojom_traits_unittest.cc @@ -25,7 +25,6 @@ TEST_F(StructTraitsTest, RendererSettings) { input.partial_swap_enabled = true; input.should_clear_root_render_pass = false; input.release_overlay_resources_after_gpu_query = true; - input.show_overdraw_feedback = true; input.highp_threshold_min = -1; input.use_skia_renderer = true; @@ -41,13 +40,27 @@ TEST_F(StructTraitsTest, RendererSettings) { output.should_clear_root_render_pass); EXPECT_EQ(input.release_overlay_resources_after_gpu_query, output.release_overlay_resources_after_gpu_query); - EXPECT_EQ(input.tint_gl_composited_content, - output.tint_gl_composited_content); - EXPECT_EQ(input.show_overdraw_feedback, output.show_overdraw_feedback); EXPECT_EQ(input.highp_threshold_min, output.highp_threshold_min); EXPECT_EQ(input.use_skia_renderer, output.use_skia_renderer); } +TEST_F(StructTraitsTest, DebugRendererSettings) { + DebugRendererSettings input; + + // Set |input| to non-default values. + input.show_overdraw_feedback = true; + input.tint_composited_content = true; + input.show_dc_layer_debug_borders = true; + + DebugRendererSettings output; + mojom::DebugRendererSettings::Deserialize( + mojom::DebugRendererSettings::Serialize(&input), &output); + EXPECT_EQ(input.show_overdraw_feedback, output.show_overdraw_feedback); + EXPECT_EQ(input.tint_composited_content, output.tint_composited_content); + EXPECT_EQ(input.show_dc_layer_debug_borders, + output.show_dc_layer_debug_borders); +} + } // namespace } // namespace viz diff --git a/chromium/services/viz/privileged/mojom/viz_main.mojom b/chromium/services/viz/privileged/mojom/viz_main.mojom index dd2ac152395..f13ce4558fe 100644 --- a/chromium/services/viz/privileged/mojom/viz_main.mojom +++ b/chromium/services/viz/privileged/mojom/viz_main.mojom @@ -8,6 +8,7 @@ import "components/discardable_memory/public/mojom/discardable_shared_memory_man import "services/network/public/mojom/tcp_socket.mojom"; import "services/viz/public/mojom/compositing/compositing_mode_watcher.mojom"; import "services/viz/privileged/mojom/compositing/frame_sink_manager.mojom"; +import "services/viz/privileged/mojom/compositing/renderer_settings.mojom"; import "services/viz/privileged/mojom/gl/gpu_host.mojom"; [EnableIf=is_win] import "services/viz/privileged/mojom/gl/info_collection_gpu_service.mojom"; @@ -30,6 +31,8 @@ struct FrameSinkManagerParams { // Viz to host interface. pending_remote<FrameSinkManagerClient> frame_sink_manager_client; + + DebugRendererSettings debug_renderer_settings; }; struct VizDevToolsParams { diff --git a/chromium/services/viz/public/cpp/compositing/delegated_ink_point_mojom_traits.cc b/chromium/services/viz/public/cpp/compositing/delegated_ink_point_mojom_traits.cc new file mode 100644 index 00000000000..5f58bc9f5e1 --- /dev/null +++ b/chromium/services/viz/public/cpp/compositing/delegated_ink_point_mojom_traits.cc @@ -0,0 +1,17 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/viz/public/cpp/compositing/delegated_ink_point_mojom_traits.h" + +namespace mojo { + +// static +bool StructTraits< + viz::mojom::DelegatedInkPointDataView, + viz::DelegatedInkPoint>::Read(viz::mojom::DelegatedInkPointDataView data, + viz::DelegatedInkPoint* out) { + return data.ReadPoint(&out->point_) && data.ReadTimestamp(&out->timestamp_); +} + +} // namespace mojo diff --git a/chromium/services/viz/public/cpp/compositing/delegated_ink_point_mojom_traits.h b/chromium/services/viz/public/cpp/compositing/delegated_ink_point_mojom_traits.h new file mode 100644 index 00000000000..628773b2d8f --- /dev/null +++ b/chromium/services/viz/public/cpp/compositing/delegated_ink_point_mojom_traits.h @@ -0,0 +1,32 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_DELEGATED_INK_POINT_MOJOM_TRAITS_H_ +#define SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_DELEGATED_INK_POINT_MOJOM_TRAITS_H_ + +#include "components/viz/common/delegated_ink_point.h" +#include "mojo/public/cpp/base/time_mojom_traits.h" +#include "services/viz/public/mojom/compositing/delegated_ink_point.mojom-shared.h" +#include "ui/gfx/geometry/mojom/geometry_mojom_traits.h" + +namespace mojo { + +template <> +struct StructTraits<viz::mojom::DelegatedInkPointDataView, + viz::DelegatedInkPoint> { + static const gfx::PointF& point(const viz::DelegatedInkPoint& input) { + return input.point(); + } + + static base::TimeTicks timestamp(const viz::DelegatedInkPoint& input) { + return input.timestamp(); + } + + static bool Read(viz::mojom::DelegatedInkPointDataView data, + viz::DelegatedInkPoint* out); +}; + +} // namespace mojo + +#endif // SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_DELEGATED_INK_POINT_MOJOM_TRAITS_H_ diff --git a/chromium/services/viz/public/cpp/compositing/mojom_traits_perftest.cc b/chromium/services/viz/public/cpp/compositing/mojom_traits_perftest.cc index 1c4605ba931..9b6ea592fb2 100644 --- a/chromium/services/viz/public/cpp/compositing/mojom_traits_perftest.cc +++ b/chromium/services/viz/public/cpp/compositing/mojom_traits_perftest.cc @@ -27,9 +27,9 @@ namespace viz { namespace { -static const int kTimeLimitMillis = 2000; +static const auto kTimeLimit = base::TimeDelta::FromSeconds(2); static const int kNumWarmupRuns = 20; -static const int kTimeCheckInterval = 10; +static const int kNumRunsPerTimeRecord = 10; enum class UseSingleSharedQuadState { YES, NO }; @@ -73,14 +73,12 @@ class VizSerializationPerfTest : public testing::Test { message.payload(), message.payload_num_bytes(), &compositor_frame); } - base::TimeTicks start = base::TimeTicks::Now(); - base::TimeTicks end = - start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis); - base::TimeTicks now = start; + base::TimeTicks now = base::TimeTicks::Now(); + base::TimeTicks end = now + kTimeLimit; base::TimeDelta min_time; size_t count = 0; - while (start < end) { - for (int i = 0; i < kTimeCheckInterval; ++i) { + for (base::TimeTicks start = now; start < end; start = now) { + for (int i = 0; i < kNumRunsPerTimeRecord; ++i) { CompositorFrame compositor_frame; mojom::CompositorFrame::Deserialize( message.payload(), message.payload_num_bytes(), &compositor_frame); @@ -92,14 +90,13 @@ class VizSerializationPerfTest : public testing::Test { if (now - start < min_time || min_time.is_zero()) min_time = now - start; - start = now; } auto reporter = SetUpReporter(story, single_sqs); reporter.AddResult(kMetricStructDeserializationTimeUs, - min_time.InMicrosecondsF() / kTimeCheckInterval); + min_time.InMicrosecondsF() / kNumRunsPerTimeRecord); reporter.AddResult(kMetricStructDeserializationThroughputRunsPerS, - count * 1000 / kTimeLimitMillis); + count * kTimeLimit.ToHz()); } static void RunSerializationTestStructTraits( @@ -111,14 +108,12 @@ class VizSerializationPerfTest : public testing::Test { mojom::CompositorFrame::SerializeAsMessage(&frame); } - base::TimeTicks start = base::TimeTicks::Now(); - base::TimeTicks end = - start + base::TimeDelta::FromMilliseconds(kTimeLimitMillis); - base::TimeTicks now = start; + base::TimeTicks now = base::TimeTicks::Now(); + base::TimeTicks end = now + kTimeLimit; base::TimeDelta min_time; size_t count = 0; - while (start < end) { - for (int i = 0; i < kTimeCheckInterval; ++i) { + for (base::TimeTicks start = now; start < end; start = now) { + for (int i = 0; i < kNumRunsPerTimeRecord; ++i) { mojo::Message message = mojom::CompositorFrame::SerializeAsMessage(&frame); now = base::TimeTicks::Now(); @@ -129,14 +124,13 @@ class VizSerializationPerfTest : public testing::Test { if (now - start < min_time || min_time.is_zero()) min_time = now - start; - start = now; } auto reporter = SetUpReporter(story, single_sqs); reporter.AddResult(kMetricStructSerializationTimeUs, - min_time.InMicrosecondsF() / kTimeCheckInterval); + min_time.InMicrosecondsF() / kNumRunsPerTimeRecord); reporter.AddResult(kMetricStructSerializationThroughputRunsPerS, - count * 1000 / kTimeLimitMillis); + count / kTimeLimit.InSecondsF()); } static void RunComplexCompositorFrameTest(const std::string& story) { @@ -199,7 +193,7 @@ class VizSerializationPerfTest : public testing::Test { SkScalar arbitrary_sigma = SkFloatToScalar(2.0f); gfx::ContentColorUsage arbitrary_content_color_usage = gfx::ContentColorUsage::kSRGB; - int root_id = 14; + RenderPassId root_id{14}; cc::FilterOperations arbitrary_filters1; arbitrary_filters1.Append( @@ -308,7 +302,8 @@ class VizSerializationPerfTest : public testing::Test { for (uint32_t i = 0; i < num_passes; ++i) { std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); - render_pass->SetNew(1, gfx::Rect(20, 20), gfx::Rect(), gfx::Transform()); + render_pass->SetNew(RenderPassId{1}, gfx::Rect(20, 20), gfx::Rect(), + gfx::Transform()); for (uint32_t j = 0; j < num_quads; ++j) { if (j == 0 || single_sqs == UseSingleSharedQuadState::NO) render_pass->CreateAndAppendSharedQuadState(); diff --git a/chromium/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/chromium/services/viz/public/cpp/compositing/mojom_traits_unittest.cc index 8398354593f..867733f09a0 100644 --- a/chromium/services/viz/public/cpp/compositing/mojom_traits_unittest.cc +++ b/chromium/services/viz/public/cpp/compositing/mojom_traits_unittest.cc @@ -448,7 +448,8 @@ TEST_F(StructTraitsTest, SharedQuadState) { // RenderPass, and QuadListBasic unit tests. TEST_F(StructTraitsTest, CompositorFrame) { std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); - render_pass->SetNew(1, gfx::Rect(5, 6), gfx::Rect(2, 3), gfx::Transform()); + render_pass->SetNew(RenderPassId{1}, gfx::Rect(5, 6), gfx::Rect(2, 3), + gfx::Transform()); // SharedQuadState. const gfx::Transform sqs_quad_to_target_transform( @@ -589,7 +590,7 @@ TEST_F(StructTraitsTest, SurfaceInfo) { } TEST_F(StructTraitsTest, ReturnedResource) { - const RenderPassId id = 1337u; + const unsigned id = 1337u; const gpu::CommandBufferNamespace command_buffer_namespace = gpu::IN_PROCESS; const gpu::CommandBufferId command_buffer_id( gpu::CommandBufferId::FromUnsafeValue(0xdeadbeef)); @@ -698,7 +699,7 @@ TEST_F(StructTraitsTest, RenderPass) { // The CopyOutputRequest struct traits require a TaskRunner. base::test::TaskEnvironment task_environment; - const RenderPassId render_pass_id = 3u; + const RenderPassId render_pass_id{3u}; const gfx::Rect output_rect(45, 22, 120, 13); const gfx::Transform transform_to_root = gfx::Transform(1.0, 0.5, 0.5, -0.5, -1.0, 0.0); @@ -855,7 +856,7 @@ TEST_F(StructTraitsTest, RenderPass) { } TEST_F(StructTraitsTest, RenderPassWithEmptySharedQuadStateList) { - const RenderPassId render_pass_id = 3u; + const RenderPassId render_pass_id{3u}; const gfx::Rect output_rect(45, 22, 120, 13); const gfx::Rect damage_rect(56, 123, 19, 43); const gfx::Transform transform_to_root = @@ -892,7 +893,8 @@ TEST_F(StructTraitsTest, RenderPassWithEmptySharedQuadStateList) { TEST_F(StructTraitsTest, QuadListBasic) { std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); - render_pass->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform()); + render_pass->SetNew(RenderPassId{1}, gfx::Rect(), gfx::Rect(), + gfx::Transform()); SharedQuadState* sqs = render_pass->CreateAndAppendSharedQuadState(); @@ -926,7 +928,7 @@ TEST_F(StructTraitsTest, QuadListBasic) { const gfx::Rect rect4(1234, 5678, 91012, 13141); const bool needs_blending = true; const ResourceId resource_id4(1337); - const RenderPassId render_pass_id = 1234u; + const RenderPassId render_pass_id{1234u}; const gfx::RectF mask_uv_rect(0, 0, 1337.1f, 1234.2f); const gfx::Size mask_texture_size(1234, 5678); gfx::Vector2dF filters_scale(1234.1f, 4321.2f); @@ -1120,7 +1122,8 @@ TEST_F(StructTraitsTest, TransferableResource) { TEST_F(StructTraitsTest, YUVDrawQuad) { std::unique_ptr<RenderPass> render_pass = RenderPass::Create(); - render_pass->SetNew(1, gfx::Rect(), gfx::Rect(), gfx::Transform()); + render_pass->SetNew(RenderPassId{1}, gfx::Rect(), gfx::Rect(), + gfx::Transform()); const DrawQuad::Material material = DrawQuad::Material::kYuvVideoContent; const gfx::Rect rect(1234, 4321, 1357, 7531); diff --git a/chromium/services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc b/chromium/services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc index 5da0674505e..6a8155c776a 100644 --- a/chromium/services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc +++ b/chromium/services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc @@ -47,11 +47,11 @@ bool StructTraits<viz::mojom::PaintFilterDataView, sk_sp<cc::PaintFilter>>:: } // We don't need to populate the DeserializeOptions here since the security - // constraints explicitly disable serializing images using the transfer cache - // and serialization of PaintRecords. + // constraints explicitly disable serializing images using the transfer + // cache/gpu::Mailbox and serialization of PaintRecords. std::vector<uint8_t> scratch_buffer; cc::PaintOp::DeserializeOptions options(nullptr, nullptr, nullptr, - &scratch_buffer, false); + &scratch_buffer, false, nullptr); cc::PaintOpReader reader(buffer->data(), buffer->size(), options, true /* enable_security_constraints */); sk_sp<cc::PaintFilter> filter; diff --git a/chromium/services/viz/public/cpp/compositing/quads_mojom_traits.cc b/chromium/services/viz/public/cpp/compositing/quads_mojom_traits.cc index c99d11f218a..a0e9a99df38 100644 --- a/chromium/services/viz/public/cpp/compositing/quads_mojom_traits.cc +++ b/chromium/services/viz/public/cpp/compositing/quads_mojom_traits.cc @@ -4,6 +4,7 @@ #include "services/viz/public/cpp/compositing/quads_mojom_traits.h" +#include "services/viz/public/cpp/compositing/render_pass_id_mojom_traits.h" #include "services/viz/public/cpp/crash_keys.h" #include "ui/gfx/mojom/color_space_mojom_traits.h" #include "ui/gfx/mojom/transform_mojom_traits.h" @@ -73,17 +74,17 @@ bool StructTraits<viz::mojom::RenderPassQuadStateDataView, viz::DrawQuad>::Read( quad->resources.ids[viz::RenderPassDrawQuad::kMaskResourceIdIndex] = data.mask_resource_id(); quad->resources.count = data.mask_resource_id() ? 1 : 0; - quad->render_pass_id = data.render_pass_id(); - // RenderPass ids are never zero. - if (!quad->render_pass_id) { - viz::SetDeserializationCrashKeyString("Draw quad invalid render pass ID"); - return false; - } if (!data.ReadMaskUvRect(&quad->mask_uv_rect) || !data.ReadMaskTextureSize(&quad->mask_texture_size) || !data.ReadFiltersScale(&quad->filters_scale) || !data.ReadFiltersOrigin(&quad->filters_origin) || - !data.ReadTexCoordRect(&quad->tex_coord_rect)) { + !data.ReadTexCoordRect(&quad->tex_coord_rect) || + !data.ReadRenderPassId(&quad->render_pass_id)) { + return false; + } + // RenderPass ids are never zero. + if (!quad->render_pass_id) { + viz::SetDeserializationCrashKeyString("Draw quad invalid render pass ID"); return false; } quad->force_anti_aliasing_off = data.force_anti_aliasing_off(); @@ -157,6 +158,7 @@ bool StructTraits<viz::mojom::TextureQuadStateDataView, viz::DrawQuad>::Read( quad->y_flipped = data.y_flipped(); quad->nearest_neighbor = data.nearest_neighbor(); quad->secure_output_only = data.secure_output_only(); + quad->is_video_frame = data.is_video_frame(); return true; } diff --git a/chromium/services/viz/public/cpp/compositing/quads_mojom_traits.h b/chromium/services/viz/public/cpp/compositing/quads_mojom_traits.h index a690016318a..fb60ffff732 100644 --- a/chromium/services/viz/public/cpp/compositing/quads_mojom_traits.h +++ b/chromium/services/viz/public/cpp/compositing/quads_mojom_traits.h @@ -198,7 +198,7 @@ struct StructTraits<viz::mojom::DebugBorderQuadStateDataView, viz::DrawQuad> { template <> struct StructTraits<viz::mojom::RenderPassQuadStateDataView, viz::DrawQuad> { - static uint64_t render_pass_id(const viz::DrawQuad& input) { + static viz::RenderPassId render_pass_id(const viz::DrawQuad& input) { const viz::RenderPassDrawQuad* quad = viz::RenderPassDrawQuad::MaterialCast(&input); DCHECK(quad->render_pass_id); @@ -409,6 +409,12 @@ struct StructTraits<viz::mojom::TextureQuadStateDataView, viz::DrawQuad> { return quad->secure_output_only; } + static bool is_video_frame(const viz::DrawQuad& input) { + const viz::TextureDrawQuad* quad = + viz::TextureDrawQuad::MaterialCast(&input); + return quad->is_video_frame; + } + static gfx::ProtectedVideoType protected_video_type( const viz::DrawQuad& input) { const viz::TextureDrawQuad* quad = diff --git a/chromium/services/viz/public/cpp/compositing/render_pass_id_mojom_traits.cc b/chromium/services/viz/public/cpp/compositing/render_pass_id_mojom_traits.cc new file mode 100644 index 00000000000..dd04b80256a --- /dev/null +++ b/chromium/services/viz/public/cpp/compositing/render_pass_id_mojom_traits.cc @@ -0,0 +1,25 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/viz/public/cpp/compositing/render_pass_id_mojom_traits.h" + +#include "components/viz/common/quads/render_pass.h" + +namespace mojo { + +// static +uint64_t StructTraits<viz::mojom::RenderPassIdDataView, + viz::RenderPassId>::value(const viz::RenderPassId& id) { + return static_cast<uint64_t>(id); +} + +// static +bool StructTraits<viz::mojom::RenderPassIdDataView, viz::RenderPassId>::Read( + viz::mojom::RenderPassIdDataView data, + viz::RenderPassId* out) { + *out = viz::RenderPassId{data.value()}; + return true; +} + +} // namespace mojo diff --git a/chromium/services/viz/public/cpp/compositing/render_pass_id_mojom_traits.h b/chromium/services/viz/public/cpp/compositing/render_pass_id_mojom_traits.h new file mode 100644 index 00000000000..fb875678915 --- /dev/null +++ b/chromium/services/viz/public/cpp/compositing/render_pass_id_mojom_traits.h @@ -0,0 +1,23 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_RENDER_PASS_ID_MOJOM_TRAITS_H_ +#define SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_RENDER_PASS_ID_MOJOM_TRAITS_H_ + +#include "components/viz/common/quads/render_pass.h" +#include "services/viz/public/mojom/compositing/render_pass_id.mojom-shared.h" + +namespace mojo { + +template <> +struct StructTraits<viz::mojom::RenderPassIdDataView, viz::RenderPassId> { + static uint64_t value(const viz::RenderPassId& id); + + static bool Read(viz::mojom::RenderPassIdDataView data, + viz::RenderPassId* out); +}; + +} // namespace mojo + +#endif // SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_RENDER_PASS_ID_MOJOM_TRAITS_H_ diff --git a/chromium/services/viz/public/cpp/compositing/render_pass_mojom_traits.cc b/chromium/services/viz/public/cpp/compositing/render_pass_mojom_traits.cc index e704ba1005d..b56bb984209 100644 --- a/chromium/services/viz/public/cpp/compositing/render_pass_mojom_traits.cc +++ b/chromium/services/viz/public/cpp/compositing/render_pass_mojom_traits.cc @@ -5,6 +5,8 @@ #include "services/viz/public/cpp/compositing/render_pass_mojom_traits.h" #include "base/numerics/safe_conversions.h" +#include "components/viz/common/quads/render_pass.h" +#include "services/viz/public/cpp/compositing/render_pass_id_mojom_traits.h" #include "services/viz/public/cpp/crash_keys.h" #include "ui/gfx/mojom/display_color_spaces_mojom_traits.h" @@ -23,10 +25,10 @@ bool StructTraits<viz::mojom::RenderPassDataView, !data.ReadBackdropFilters(&(*out)->backdrop_filters) || !data.ReadBackdropFilterBounds(&(*out)->backdrop_filter_bounds) || !data.ReadContentColorUsage(&(*out)->content_color_usage) || - !data.ReadCopyRequests(&(*out)->copy_requests)) { + !data.ReadCopyRequests(&(*out)->copy_requests) || + !data.ReadId(&(*out)->id)) { return false; } - (*out)->id = data.id(); // RenderPass ids are never zero. if (!(*out)->id) { viz::SetDeserializationCrashKeyString("Invalid render pass ID"); diff --git a/chromium/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h b/chromium/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h index 4153d90cf16..8301911110e 100644 --- a/chromium/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h +++ b/chromium/services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h @@ -71,6 +71,10 @@ struct StructTraits<viz::mojom::SharedQuadStateDataView, OptSharedQuadState> { static float de_jelly_delta_y(const OptSharedQuadState& input) { return input.sqs->de_jelly_delta_y; } + + static bool no_damage(const OptSharedQuadState& input) { + return input.sqs->no_damage; + } }; template <> @@ -124,6 +128,10 @@ struct StructTraits<viz::mojom::SharedQuadStateDataView, viz::SharedQuadState> { return sqs.de_jelly_delta_y; } + static bool no_damage(const viz::SharedQuadState& sqs) { + return sqs.no_damage; + } + static bool Read(viz::mojom::SharedQuadStateDataView data, viz::SharedQuadState* out) { if (!data.ReadQuadToTargetTransform(&out->quad_to_target_transform) || @@ -143,6 +151,7 @@ struct StructTraits<viz::mojom::SharedQuadStateDataView, viz::SharedQuadState> { out->sorting_context_id = data.sorting_context_id(); out->is_fast_rounded_corner = data.is_fast_rounded_corner(); out->de_jelly_delta_y = data.de_jelly_delta_y(); + out->no_damage = data.no_damage(); return true; } }; diff --git a/chromium/services/viz/public/cpp/gpu/context_provider_command_buffer.cc b/chromium/services/viz/public/cpp/gpu/context_provider_command_buffer.cc index 0ed0af524d7..3e02b1983c0 100644 --- a/chromium/services/viz/public/cpp/gpu/context_provider_command_buffer.cc +++ b/chromium/services/viz/public/cpp/gpu/context_provider_command_buffer.cc @@ -45,7 +45,7 @@ #include "services/viz/public/cpp/gpu/command_buffer_metrics.h" #include "skia/buildflags.h" #include "third_party/skia/include/core/SkTraceMemoryDump.h" -#include "third_party/skia/include/gpu/GrContext.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" #include "ui/gl/trace_util.h" class SkDiscardableMemory; @@ -374,7 +374,7 @@ gpu::ContextSupport* ContextProviderCommandBuffer::ContextSupport() { return impl_; } -class GrContext* ContextProviderCommandBuffer::GrContext() { +class GrDirectContext* ContextProviderCommandBuffer::GrContext() { DCHECK(bind_tried_); DCHECK_EQ(bind_result_, gpu::ContextResult::kSuccess); DCHECK(support_grcontext_); diff --git a/chromium/services/viz/public/cpp/gpu/context_provider_command_buffer.h b/chromium/services/viz/public/cpp/gpu/context_provider_command_buffer.h index 18918b5494e..22d80baf765 100644 --- a/chromium/services/viz/public/cpp/gpu/context_provider_command_buffer.h +++ b/chromium/services/viz/public/cpp/gpu/context_provider_command_buffer.h @@ -92,7 +92,7 @@ class ContextProviderCommandBuffer gpu::gles2::GLES2Interface* ContextGL() override; gpu::raster::RasterInterface* RasterInterface() override; gpu::ContextSupport* ContextSupport() override; - class GrContext* GrContext() override; + class GrDirectContext* GrContext() override; gpu::SharedImageInterface* SharedImageInterface() override; ContextCacheController* CacheController() override; base::Lock* GetLock() override; diff --git a/chromium/services/viz/public/mojom/BUILD.gn b/chromium/services/viz/public/mojom/BUILD.gn index d6d676deaba..1cec0dae39d 100644 --- a/chromium/services/viz/public/mojom/BUILD.gn +++ b/chromium/services/viz/public/mojom/BUILD.gn @@ -16,6 +16,7 @@ mojom("mojom") { "compositing/copy_output_request.mojom", "compositing/copy_output_result.mojom", "compositing/delegated_ink_metadata.mojom", + "compositing/delegated_ink_point.mojom", "compositing/filter_operation.mojom", "compositing/filter_operations.mojom", "compositing/frame_deadline.mojom", @@ -26,6 +27,7 @@ mojom("mojom") { "compositing/paint_filter.mojom", "compositing/quads.mojom", "compositing/render_pass.mojom", + "compositing/render_pass_id.mojom", "compositing/resource_settings.mojom", "compositing/returned_resource.mojom", "compositing/selection.mojom", @@ -129,6 +131,16 @@ mojom("mojom") { "//ui/gfx/geometry/mojom", ] }, + { + types = [ + { + mojom = "viz.mojom.LocalSurfaceIdAllocation" + cpp = "::viz::LocalSurfaceIdAllocation" + }, + ] + traits_headers = [ "//services/viz/public/cpp/compositing/local_surface_id_allocation_mojom_traits.h" ] + traits_public_deps = [ "//components/viz/common" ] + }, ] cpp_typemaps = shared_cpp_typemaps @@ -240,6 +252,17 @@ mojom("mojom") { { types = [ { + mojom = "viz.mojom.DelegatedInkPoint" + cpp = "::viz::DelegatedInkPoint" + }, + ] + traits_sources = [ "//services/viz/public/cpp/compositing/delegated_ink_point_mojom_traits.cc" ] + traits_headers = [ "//services/viz/public/cpp/compositing/delegated_ink_point_mojom_traits.h" ] + traits_public_deps = [ "//components/viz/common" ] + }, + { + types = [ + { mojom = "viz.mojom.FilterOperation" cpp = "::cc::FilterOperation" }, @@ -309,27 +332,32 @@ mojom("mojom") { { types = [ { - mojom = "viz.mojom.LocalSurfaceIdAllocation" - cpp = "::viz::LocalSurfaceIdAllocation" + mojom = "viz.mojom.PaintFilter" + cpp = "::sk_sp<::cc::PaintFilter>" }, ] - traits_headers = [ "//services/viz/public/cpp/compositing/local_surface_id_allocation_mojom_traits.h" ] - traits_public_deps = [ "//components/viz/common" ] + traits_sources = [ + "//services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc", + ] + traits_headers = [ + "//services/viz/public/cpp/compositing/paint_filter_mojom_traits.h", + ] + traits_public_deps = [ "//cc/paint" ] }, { types = [ { - mojom = "viz.mojom.PaintFilter" - cpp = "::sk_sp<::cc::PaintFilter>" + mojom = "viz.mojom.RenderPassId" + cpp = "::viz::RenderPassId" }, ] traits_sources = [ - "//services/viz/public/cpp/compositing/paint_filter_mojom_traits.cc", + "//services/viz/public/cpp/compositing/render_pass_id_mojom_traits.cc", ] traits_headers = [ - "//services/viz/public/cpp/compositing/paint_filter_mojom_traits.h", + "//services/viz/public/cpp/compositing/render_pass_id_mojom_traits.h", ] - traits_public_deps = [ "//cc/paint" ] + traits_public_deps = [ "//components/viz/common" ] }, { types = [ @@ -509,6 +537,7 @@ mojom("mojom") { "//services/viz/public/cpp/compositing/filter_operation_mojom_traits.h", "//services/viz/public/cpp/compositing/filter_operations_mojom_traits.h", "//services/viz/public/cpp/compositing/quads_mojom_traits.h", + "//services/viz/public/cpp/compositing/render_pass_id_mojom_traits.h", "//services/viz/public/cpp/compositing/render_pass_mojom_traits.h", "//services/viz/public/cpp/compositing/selection_mojom_traits.h", "//services/viz/public/cpp/compositing/shared_quad_state_mojom_traits.h", diff --git a/chromium/services/viz/public/mojom/compositing/delegated_ink_point.mojom b/chromium/services/viz/public/mojom/compositing/delegated_ink_point.mojom new file mode 100644 index 00000000000..1335e5e9ab7 --- /dev/null +++ b/chromium/services/viz/public/mojom/compositing/delegated_ink_point.mojom @@ -0,0 +1,25 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module viz.mojom; + +import "mojo/public/mojom/base/time.mojom"; +import "ui/gfx/geometry/mojom/geometry.mojom"; + +// See components/viz/common/delegated_ink_point.h. +struct DelegatedInkPoint { + gfx.mojom.PointF point; + mojo_base.mojom.TimeTicks timestamp; +}; + +// This interface is used to connect the browser process to viz to support +// delegated ink trails. A delegated ink point will be produced in the +// browser process and sent to viz to be held until DrawAndSwap occurs, at +// which point any delegated ink points that arrived may be used to draw the +// ink trail. +interface DelegatedInkPointRenderer { + // Used to send the DelegatedInkPoint that was created in the browser process + // to viz in order to be drawn as part of the delegated ink trail. + StoreDelegatedInkPoint(DelegatedInkPoint point); +}; diff --git a/chromium/services/viz/public/mojom/compositing/quads.mojom b/chromium/services/viz/public/mojom/compositing/quads.mojom index ba8bac8ee6b..58ec1757c9a 100644 --- a/chromium/services/viz/public/mojom/compositing/quads.mojom +++ b/chromium/services/viz/public/mojom/compositing/quads.mojom @@ -5,6 +5,7 @@ module viz.mojom; import "mojo/public/mojom/base/unguessable_token.mojom"; +import "services/viz/public/mojom/compositing/render_pass_id.mojom"; import "services/viz/public/mojom/compositing/shared_quad_state.mojom"; import "services/viz/public/mojom/compositing/surface_range.mojom"; import "ui/gfx/geometry/mojom/geometry.mojom"; @@ -26,7 +27,7 @@ struct DebugBorderQuadState { }; struct RenderPassQuadState { - uint64 render_pass_id; + RenderPassId render_pass_id; // If nonzero, resource id of mask to use when drawing this pass. uint32 mask_resource_id; @@ -86,6 +87,7 @@ struct TextureQuadState { bool y_flipped; bool nearest_neighbor; bool secure_output_only; + bool is_video_frame; ProtectedVideoState protected_video_type; }; diff --git a/chromium/services/viz/public/mojom/compositing/render_pass.mojom b/chromium/services/viz/public/mojom/compositing/render_pass.mojom index ab38c6396ae..3590a80fd7e 100644 --- a/chromium/services/viz/public/mojom/compositing/render_pass.mojom +++ b/chromium/services/viz/public/mojom/compositing/render_pass.mojom @@ -7,6 +7,7 @@ module viz.mojom; import "services/viz/public/mojom/compositing/copy_output_request.mojom"; import "services/viz/public/mojom/compositing/filter_operations.mojom"; import "services/viz/public/mojom/compositing/quads.mojom"; +import "services/viz/public/mojom/compositing/render_pass_id.mojom"; import "ui/gfx/geometry/mojom/geometry.mojom"; import "ui/gfx/mojom/display_color_spaces.mojom"; import "ui/gfx/mojom/rrect_f.mojom"; @@ -14,7 +15,7 @@ import "ui/gfx/mojom/transform.mojom"; // See components/viz/common/quads/render_pass.h. struct RenderPass { - uint64 id; + RenderPassId id; gfx.mojom.Rect output_rect; gfx.mojom.Rect damage_rect; gfx.mojom.Transform transform_to_root_target; diff --git a/chromium/services/viz/public/mojom/compositing/render_pass_id.mojom b/chromium/services/viz/public/mojom/compositing/render_pass_id.mojom new file mode 100644 index 00000000000..b82f2034e85 --- /dev/null +++ b/chromium/services/viz/public/mojom/compositing/render_pass_id.mojom @@ -0,0 +1,9 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module viz.mojom; + +struct RenderPassId { + uint64 value; +}; diff --git a/chromium/services/viz/public/mojom/compositing/shared_quad_state.mojom b/chromium/services/viz/public/mojom/compositing/shared_quad_state.mojom index 12062fb582a..214f508b306 100644 --- a/chromium/services/viz/public/mojom/compositing/shared_quad_state.mojom +++ b/chromium/services/viz/public/mojom/compositing/shared_quad_state.mojom @@ -43,4 +43,9 @@ struct SharedQuadState { // The y offset by which to skew quads in this layer. For experimental // de-jelly effect. float de_jelly_delta_y; + + // If true, indicates that the quads do not contribute damage to their + // render pass's damage; if false, whether or not the quads contribute + // damage is unknown. + bool no_damage; }; |