0

fuzzing: introduce automated IPC fuzzing

This CL introduces a fuzzer that automatically detects renderer process
exposed IPC interfaces and fuzzes them.

The design of this fuzzer has been described at
https://docs.google.com/document/d/1Y5yy8YTJRLPQUPHzPO-KhmT-gZoBkLjWp2eRaJbBpq4/edit?usp=sharing.

Because GN does not support dynamic dependencies, this patch needs to
introduce a gni file at `chrome/browser_exposed_mojom_targets.gni`
which lists all the targets of browser exposed mojom interfaces on
Linux. Please, check this doc to read about pros and cons about the
different considered solutions:
https://docs.google.com/document/d/1BSgrxtXVKGEtVU5KoKZbvZfQOxIZb2fqYcAX4FXYw8E.

Thanks to this automated IPC fuzzer, any new browser-exposed IPC
endpoint will be fuzzed as soon as it gets in the codebase. Doing this,
we aim at detecting potential issues as soon as possible.

Bug: 40282115
Cq-Include-Trybots: luci.chromium.try:linux-centipede-asan-rel
Change-Id: I1f097b871d53c4f2a34c62c43bbede1fd23d760a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5490095
Reviewed-by: Adrian Taylor <adetaylor@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Paul Semel <paulsemel@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1310532}
This commit is contained in:
Paul Semel
2024-06-05 10:57:35 +00:00
committed by Chromium LUCI CQ
parent 11a700e804
commit 90ef9055a5
7 changed files with 850 additions and 1304 deletions

@ -0,0 +1,345 @@
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This file represents global knowledge about all the mojom targets building
# browser-to-renderer exposed mojom interfaces. Developers are responsible for
# maintaining this file to match addition and removal of newly exposed targets.
# This is part of our automated fuzzing of the browser/renderer interface that
# lives at `//chrome/test/fuzzing/renderer_fuzzing/ipc_fuzzing`.
# As for now, we are only listing those targets for interfaces exposed on
# Linux.
browser_exposed_mojom_targets = [
"//cc/mojom:layer_type",
"//cc/mojom:mojom",
"//chrome/browser/cart:mojo_bindings",
"//chrome/browser/companion/core/mojom:mojo_bindings",
"//chrome/browser/lens/core/mojom:mojo_bindings",
"//chrome/browser/media:mojo_bindings",
"//chrome/browser/new_tab_page/modules/feed:mojo_bindings",
"//chrome/browser/new_tab_page/modules/file_suggestion:mojo_bindings",
"//chrome/browser/new_tab_page/modules/history_clusters:mojo_bindings",
"//chrome/browser/new_tab_page/modules/history_clusters/cart:mojo_bindings",
"//chrome/browser/new_tab_page/modules/history_clusters/discount:mojo_bindings",
"//chrome/browser/new_tab_page/modules/photos:mojo_bindings",
"//chrome/browser/new_tab_page/modules/recipes:mojo_bindings",
"//chrome/browser/new_tab_page/modules/safe_browsing:mojo_bindings",
"//chrome/browser/new_tab_page/modules/v2/calendar:mojo_bindings",
"//chrome/browser/new_tab_page/modules/v2/history_clusters:mojo_bindings",
"//chrome/browser/new_tab_page/modules/v2/most_relevant_tab_resumption:mojo_bindings",
"//chrome/browser/new_tab_page/modules/v2/tab_resumption:mojo_bindings",
"//chrome/browser/resource_coordinator:mojo_bindings",
"//chrome/browser/ui/webui/access_code_cast:mojo_bindings",
"//chrome/browser/ui/webui/app_home:mojo_bindings",
"//chrome/browser/ui/webui/app_service_internals:mojo_bindings",
"//chrome/browser/ui/webui/bluetooth_internals:mojo_bindings",
"//chrome/browser/ui/webui/connectors_internals:mojo_bindings",
"//chrome/browser/ui/webui/data_sharing_internals:mojo_bindings",
"//chrome/browser/ui/webui/discards:mojo_bindings",
"//chrome/browser/ui/webui/downloads:mojo_bindings",
"//chrome/browser/ui/webui/hats:mojo_bindings",
"//chrome/browser/ui/webui/internals/user_education:mojo_bindings",
"//chrome/browser/ui/webui/location_internals:mojo_bindings",
"//chrome/browser/ui/webui/new_tab_page_third_party:mojo_bindings",
"//chrome/browser/ui/webui/new_tab_page:mojo_bindings",
"//chrome/browser/ui/webui/new_tab_page/foo:mojo_bindings",
"//chrome/browser/ui/webui/omnibox:mojo_bindings",
"//chrome/browser/ui/webui/on_device_internals:mojom",
"//chrome/browser/ui/webui/privacy_sandbox:mojo_bindings",
"//chrome/browser/ui/webui/reset_password:mojo_bindings",
"//chrome/browser/ui/webui/search_engine_choice:mojo_bindings",
"//chrome/browser/ui/webui/segmentation_internals:mojo_bindings",
"//chrome/browser/ui/webui/side_panel/bookmarks:mojo_bindings",
"//chrome/browser/ui/webui/side_panel/customize_chrome:mojo_bindings",
"//chrome/browser/ui/webui/side_panel/performance_controls:mojo_bindings",
"//chrome/browser/ui/webui/side_panel/reading_list:mojo_bindings",
"//chrome/browser/ui/webui/suggest_internals:mojo_bindings",
"//chrome/browser/ui/webui/tab_search:mojo_bindings",
"//chrome/browser/ui/webui/tab_strip:mojo_bindings",
"//chrome/browser/ui/webui/tabs:mojo_bindings",
"//chrome/browser/ui/webui/usb_internals:mojo_bindings",
"//chrome/browser/ui/webui/web_app_internals:mojo_bindings",
"//chrome/browser/ui/webui/whats_new:mojo_bindings",
"//chrome/browser/web_applications/mojom:mojom_web_apps_enum",
"//chrome/common:available_offline_content_mojom",
"//chrome/common:mojo_bindings",
"//chrome/common:offline_page_auto_fetcher_mojom",
"//chrome/common:supervised_user_commands_mojom",
"//chrome/common/accessibility:mojo_bindings",
"//chrome/common/cart:mojo_bindings",
"//chrome/common/companion:mojo_bindings",
"//chrome/common/compose:mojo_bindings",
"//chrome/common/importer:interfaces",
"//chrome/common/search:mojo_bindings",
"//chrome/services/file_util/public/mojom:mojom",
"//chrome/services/media_gallery_util/public/mojom:mojom",
"//chrome/services/on_device_translation/public/mojom:mojom",
"//chrome/services/printing/public/mojom:mojom",
"//chrome/services/removable_storage_writer/public/mojom:mojom",
"//components/attribution_reporting:mojom",
"//components/attribution_reporting:registration_header_error_mojom",
"//components/attribution_reporting:registration_mojom",
"//components/attribution_reporting:source_type_mojom",
"//components/autofill/content/common/mojom:mojom",
"//components/autofill/core/common/mojom:mojo_types",
"//components/browsing_topics/mojom:mojo_bindings",
"//components/commerce/core/internals/mojom:mojo_bindings",
"//components/commerce/core/mojom:mojo_bindings",
"//components/compose/core/browser:mojo_bindings",
"//components/content_capture/common:mojo_interfaces",
"//components/content_capture/common:mojo_types",
"//components/content_settings/common:mojom",
"//components/content_settings/core/common:content_settings_enums",
"//components/content_settings/core/common:content_settings_types",
"//components/content_settings/core/common:mojo_bindings",
"//components/continuous_search/common/public/mojom:mojom",
"//components/device_signals/core/common/mojom:mojom",
"//components/digital_goods/mojom:mojom",
"//components/discardable_memory/public/mojom:mojom",
"//components/dom_distiller/content/common/mojom:mojom",
"//components/dom_distiller/core/mojom:mojom",
"//components/download/public/common:interfaces",
"//components/facilitated_payments/core/mojom:facilitated_payments_agent_mojom",
"//components/facilitated_payments/core/mojom:pix_code_validator_mojom",
"//components/feed/mojom:mojo_bindings",
"//components/global_media_controls/public/mojom:device_service",
"//components/guest_view/common:mojom",
"//components/heap_profiling/in_process:mojom",
"//components/history_clusters/history_clusters_internals/webui:mojo_bindings",
"//components/history_clusters/public/mojom:mojo_bindings",
"//components/history/core/browser/mojom:mojo_bindings",
"//components/lens:lens_mojo",
"//components/media_router/common/mojom:debugger",
"//components/media_router/common/mojom:logger",
"//components/media_router/common/mojom:media_controller",
"//components/media_router/common/mojom:media_route_provider_id",
"//components/media_router/common/mojom:media_router",
"//components/media_router/common/mojom:route_request_result_code",
"//components/metrics/public/mojom:call_stack_mojo_bindings",
"//components/metrics/public/mojom:histogram_fetcher_mojo_bindings",
"//components/metrics/public/mojom:single_sample_metrics_mojo_bindings",
"//components/metrics/structured/mojom:mojom",
"//components/mirroring/mojom:common",
"//components/mirroring/mojom:service",
"//components/ml/mojom:mojom",
"//components/ml/webnn:features",
"//components/network_hints/common:mojo_bindings",
"//components/no_state_prefetch/common:mojo_bindings",
"//components/omnibox/browser:mojo_bindings",
"//components/optimization_guide/content/mojom:mojo_interfaces",
"//components/optimization_guide/core:interfaces",
"//components/optimization_guide/optimization_guide_internals/webui:mojo_bindings",
"//components/os_crypt/async/common:algorithm_mojom",
"//components/os_crypt/async/common:common_mojom",
"//components/page_image_service/mojom:mojo_bindings",
"//components/page_load_metrics/common:page_load_metrics_mojom",
"//components/paint_preview/common/mojom:mojom",
"//components/password_manager/services/csv_password/public/mojom:mojom",
"//components/payments/mojom:mojom",
"//components/performance_manager/public/mojom:mojom",
"//components/printing/common:mojo_interfaces",
"//components/safe_browsing/content/common:interfaces",
"//components/safe_browsing/core/common:interfaces",
"//components/schema_org/common:improved_mojom",
"//components/schema_org/common:mojom",
"//components/security_interstitials/core/common/mojom:mojom",
"//components/services/filesystem/public/mojom:mojom",
"//components/services/font/public/mojom:mojom",
"//components/services/heap_profiling/public/mojom:mojom",
"//components/services/language_detection/public/mojom:mojom",
"//components/services/paint_preview_compositor/public/mojom:mojom",
"//components/services/patch/public/mojom:mojom",
"//components/services/print_compositor/public/mojom:mojom",
"//components/services/quarantine/public/mojom:mojom",
"//components/services/storage/privileged/mojom:mojom_bucket",
"//components/services/storage/privileged/mojom:mojom",
"//components/services/storage/public/mojom:mojom",
"//components/services/storage/public/mojom/buckets:buckets",
"//components/services/storage/public/mojom/filesystem:filesystem",
"//components/services/unzip/public/mojom:mojom",
"//components/site_engagement/core/mojom:mojo_bindings",
"//components/spellcheck/common:interfaces",
"//components/subresource_filter/content/mojom:mojom",
"//components/subresource_filter/core/mojom:mojom",
"//components/tab_groups/public/mojom:mojo_bindings",
"//components/translate/content/common:common",
"//components/variations:variations_mojom",
"//components/visitedlink/common:interfaces",
"//components/viz/service/debugger/mojom:mojom",
"//components/web_cache/public/mojom:mojom",
"//components/web_package/mojom:mojom",
"//components/webapps/common:mojo_bindings",
"//components/webapps/services/web_app_origin_association/public/mojom:mojom",
"//content/browser/attribution_reporting:internals_mojo_bindings",
"//content/browser/attribution_reporting:mojo_bindings",
"//content/browser/attribution_reporting:registration_result_mojom",
"//content/browser/indexed_db:internals_mojo_bindings",
"//content/browser/private_aggregation:mojo_bindings",
"//content/browser/process_internals:mojo_bindings",
"//content/browser/tracing/trace_report:mojo_bindings",
"//content/browser/xr/webxr_internals/mojom:mojo_bindings",
"//content/common:mojo_bindings",
"//content/public/common:interfaces",
"//content/public/common:renderer_type",
"//device/bluetooth/public/mojom:deprecated_experimental_interfaces",
"//device/bluetooth/public/mojom:mojom",
"//device/gamepad/public/mojom:mojom",
"//device/vr/public/mojom:isolated_xr_service",
"//device/vr/public/mojom:test_mojom",
"//device/vr/public/mojom:vr_service",
"//device/vr/public/mojom:xr_common",
"//extensions/common:mojom",
"//extensions/common/api:mojom",
"//gpu/ipc/common:gmb_interface",
"//gpu/ipc/common:gpu_channel_mojom",
"//gpu/ipc/common:gpu_preferences_interface",
"//gpu/ipc/common:interfaces",
"//gpu/ipc/common:surface_handle",
"//gpu/ipc/common:vulkan_interface",
"//ipc:mojom_constants",
"//ipc:mojom",
"//media/capture/mojom:image_capture",
"//media/capture/mojom:video_capture_buffer",
"//media/capture/mojom:video_capture_types",
"//media/capture/mojom:video_capture",
"//media/capture/mojom:video_effects_manager",
"//media/learning/mojo/public/mojom:mojom",
"//media/midi:mojo",
"//media/mojo/mojom:audio_data",
"//media/mojo/mojom:encryption_pattern",
"//media/mojo/mojom:mojom",
"//media/mojo/mojom:remoting_common",
"//media/mojo/mojom:remoting",
"//media/mojo/mojom:speech_recognition_audio_forwarder",
"//media/mojo/mojom:speech_recognition",
"//media/mojo/mojom:web_speech_recognition",
"//media/mojo/mojom/stable:native_pixmap_handle",
"//media/mojo/mojom/stable:stable_video_decoder",
"//mojo/public/interfaces/bindings:bindings",
"//mojo/public/mojom/base:base",
"//mojo/public/mojom/base:protobuf_support",
"//pdf/mojom:mojom",
"//printing/backend/mojom:mojom",
"//printing/mojom:mojom",
"//printing/mojom:printing_context",
"//sandbox/policy/mojom:mojom",
"//services/accessibility/public/mojom:automation_client",
"//services/accessibility/public/mojom:automation",
"//services/accessibility/public/mojom:mojom",
"//services/audio/public/mojom:mojom",
"//services/cert_verifier/public/mojom:mojom",
"//services/data_decoder/public/mojom:mojom_xml_parser",
"//services/data_decoder/public/mojom:mojom",
"//services/device/public/mojom:device_service",
"//services/device/public/mojom:generic_sensor",
"//services/device/public/mojom:geolocation_internals",
"//services/device/public/mojom:geoposition",
"//services/device/public/mojom:mojom",
"//services/device/public/mojom:usb_test",
"//services/device/public/mojom:usb",
"//services/image_annotation/public/mojom:mojom",
"//services/media_session/public/mojom:mojom",
"//services/metrics/public/mojom:mojom",
"//services/network/public/mojom:cookies_mojom",
"//services/network/public/mojom:mojom_attribution",
"//services/network/public/mojom:mojom_first_party_sets",
"//services/network/public/mojom:mojom_host_resolver",
"//services/network/public/mojom:mojom_ip_address",
"//services/network/public/mojom:mojom_network_anonymization_key",
"//services/network/public/mojom:mojom_network_isolation_key",
"//services/network/public/mojom:mojom_network_param",
"//services/network/public/mojom:mojom_proxy_config",
"//services/network/public/mojom:mojom_schemeful_site",
"//services/network/public/mojom:mojom_shared_dictionary",
"//services/network/public/mojom:mojom_structured_headers",
"//services/network/public/mojom:mojom",
"//services/network/public/mojom:url_loader_base",
"//services/network/public/mojom:websocket_mojom",
"//services/on_device_model/public/mojom:mojom",
"//services/passage_embeddings/public/mojom:mojom",
"//services/preferences/public/mojom:mojom",
"//services/proxy_resolver/public/mojom:mojom",
"//services/resource_coordinator/public/mojom:mojom",
"//services/screen_ai/public/mojom:factory",
"//services/screen_ai/public/mojom:mojom",
"//services/service_manager/public/mojom:constants",
"//services/service_manager/public/mojom:mojom",
"//services/shape_detection/public/mojom:mojom",
"//services/tracing/public/mojom:mojom",
"//services/video_capture/public/mojom:constants",
"//services/video_capture/public/mojom:mojom",
"//services/video_effects/public/mojom:mojom",
"//services/viz/privileged/mojom:mojom",
"//services/viz/privileged/mojom/compositing:compositing",
"//services/viz/privileged/mojom/gl:gl",
"//services/viz/public/mojom:mojom",
"//services/viz/public/mojom:shared_image_format",
"//services/viz/public/mojom:singleplanar_format",
"//services/webnn/public/mojom:mojom",
"//skia/public/mojom:mojom",
"//storage/browser/quota:mojo_bindings",
"//third_party/blink/public/mojom:android_mojo_bindings",
"//third_party/blink/public/mojom:authenticator_test_mojo_bindings",
"//third_party/blink/public/mojom:color_scheme_mojo_bindings",
"//third_party/blink/public/mojom:embedded_frame_sink_mojo_bindings",
"//third_party/blink/public/mojom:memory_usage_monitor_linux_mojo_bindings",
"//third_party/blink/public/mojom:mojom_core",
"//third_party/blink/public/mojom:mojom_mhtml_load_result",
"//third_party/blink/public/mojom:mojom_modules",
"//third_party/blink/public/mojom:mojom_platform",
"//third_party/blink/public/mojom:script_type_mojo_bindings",
"//third_party/blink/public/mojom:web_bluetooth_mojo_bindings",
"//third_party/blink/public/mojom:web_feature_mojo_bindings",
"//third_party/blink/public/mojom/gpu:gpu",
"//third_party/blink/public/mojom/origin_trial_feature:origin_trial_feature",
"//third_party/blink/public/mojom/origin_trial_state:origin_trial_state",
"//third_party/blink/public/mojom/private_network_device:private_network_device",
"//third_party/blink/public/mojom/quota:quota",
"//third_party/blink/public/mojom/runtime_feature_state:runtime_feature_state",
"//third_party/blink/public/mojom/service_worker:storage",
"//third_party/blink/public/mojom/storage_key:storage_key",
"//third_party/blink/public/mojom/tokens:tokens",
"//third_party/blink/public/mojom/usb:usb",
"//ui/accessibility:ax_constants_mojo",
"//ui/accessibility:ax_enums_mojo",
"//ui/accessibility:ax_features_mojo",
"//ui/accessibility/mojom:mojom",
"//ui/base/cursor/mojom:cursor_type",
"//ui/base/cursor/mojom:mojom",
"//ui/base/dragdrop/mojom:mojom",
"//ui/base/ime/mojom:mojom",
"//ui/base/mojom:mojom",
"//ui/color:mojom",
"//ui/display/mojom:mojom",
"//ui/events/mojom:event_latency_metadata_mojom",
"//ui/events/mojom:mojom",
"//ui/gfx/geometry/mojom:mojom",
"//ui/gfx/image/mojom:mojom",
"//ui/gfx/mojom:hdr_metadata",
"//ui/gfx/mojom:mojom",
"//ui/gfx/mojom:native_handle_types",
"//ui/gfx/range/mojom:mojom",
"//ui/gl/mojom:mojom",
"//ui/latency/mojom:mojom",
"//ui/ozone/platform/wayland/mojom:mojom",
"//ui/ozone/public/mojom:gesture_properties_service",
"//ui/webui/resources/cr_components/app_management:mojo_bindings",
"//ui/webui/resources/cr_components/certificate_manager:mojom",
"//ui/webui/resources/cr_components/color_change_listener:mojom",
"//ui/webui/resources/cr_components/commerce:mojo_bindings",
"//ui/webui/resources/cr_components/customize_color_scheme_mode:mojom",
"//ui/webui/resources/cr_components/customize_themes:mojom",
"//ui/webui/resources/cr_components/help_bubble:mojo_bindings",
"//ui/webui/resources/cr_components/history_clusters:mojo_bindings",
"//ui/webui/resources/cr_components/history_embeddings:mojo_bindings",
"//ui/webui/resources/cr_components/most_visited:mojom",
"//ui/webui/resources/cr_components/searchbox:mojo_bindings",
"//ui/webui/resources/cr_components/theme_color_picker:mojom",
"//ui/webui/resources/js/browser_command:mojo_bindings",
"//ui/webui/resources/js/metrics_reporter:mojo_bindings",
"//url/mojom:url_mojom_gurl",
"//url/mojom:url_mojom_origin",
"//url/mojom:url_mojom_scheme_host_port",
]

@ -2,15 +2,22 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//chrome/browser_exposed_mojom_targets.gni")
import("//chrome/test/fuzzing/in_process_fuzzer.gni")
import("//chrome/test/fuzzing/renderer_fuzzing/in_process_renderer_fuzzing.gni")
import(
"//chrome/test/fuzzing/renderer_fuzzing/ipc_fuzzing/mojom_interfaces.gni")
group("test") {
testonly = true
}
# We want to make sure to only enable this fuzzer on platforms that have a CQ
# bot so that the mojom target list is maintained up-to-date.
# Similarly, if something goes wrong with those targets, this will "only" break
# fuzzer CQ bots, so we this allows for damage control.
# crbug.com/343669713: enable this on Windows once the Linux version sticks in.
renderer_ipc_fuzzing_enabled =
fuzzing_engine_supports_custom_main && is_linux && enable_mojom_fuzzer
if (fuzzing_engine_supports_custom_main) {
source_set("renderer_in_process_fuzzer_runner") {
testonly = true
@ -25,164 +32,77 @@ if (fuzzing_engine_supports_custom_main) {
}
}
# As for now, we cannot compile this fuzzer when `dcheck_always_on=true`,
# because InProcessFuzzer aren't running properly. See crbug.com/40949031.
# Relatedly, we are also checking for `fuzzing_engine_supports_custom_main`
# because this is a necessary condition to most of the in_process_fuzzer
# dependencies, which we are heavily relying upon here.
if (!is_android && !dcheck_always_on && fuzzing_engine_supports_custom_main) {
if (renderer_ipc_fuzzing_enabled) {
_mojolpm_deps = []
foreach(target, browser_exposed_mojom_targets) {
_mojolpm_deps += [ "${target}_mojolpm" ]
}
# This tool aims at replicating an environment similar to how
# in_process_fuzzer are running, so that we can fetch a list of mojom
# interfaces that make sense for `renderer_in_process_mojolpm_fuzzer`.
executable("ipc_interfaces_dumper") {
testonly = true
sources = [
# We include those files so that we take into consideration the exact
# same setup as in_process_fuzzer, including the runtime flags. This is
# important because this can have an effect on which interfaces are
# being exposed.
"../in_process_fuzzer.cc",
"../in_process_fuzzer.h",
"ipc_fuzzing/ipc_interfaces_dumper.cc",
]
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
sources = [ "ipc_fuzzing/ipc_interfaces_dumper.cc" ]
deps = [
"//chrome/test:chrome_test_launcher",
"//base",
"//chrome/test:browser_tests_runner",
"//chrome/test:test_support",
"//chrome/test/fuzzing:in_process_fuzzer_buildflags",
"//content/test:test_support",
# This is so that in_process_fuzzer internals work, but
# `ipc_interfaces_dumper` is not recognized as an actual fuzzer.
"//testing/libfuzzer:fuzzing_engine_no_main_core",
]
}
action("renderer_in_process_mojolpm_fuzzer_generator") {
testonly = true
deps = [ ":ipc_interfaces_dumper" ]
depfile = "$target_out_dir/$target_name.d"
inputs = []
foreach(target, browser_exposed_mojom_targets) {
inputs += [ get_label_info(target, "target_gen_dir") + "/" +
get_label_info(target, "name") + ".build_metadata" ]
}
# We cannot use the GN `metadata` mechanism here, because our initial
# deps could depend on other mojom targets which would also generate some
# metadata, but we would actually not depend on their `mojolpm` variant.
# Doing things the current way allows for ensuring that we are only listing
# meta files for mojolpm targets we directly depend upon.
_metafiles = []
foreach(file, inputs) {
_metafiles += [ rebase_path(file, root_build_dir) ]
}
write_file("$target_gen_dir/metadata", _metafiles)
script = "//chrome/test/fuzzing/renderer_fuzzing/ipc_fuzzing/generate_testcase.py"
args = [
"-n",
"renderer_in_process_mojolpm_fuzzer",
"-p",
rebase_path("${root_build_dir}/ipc_interfaces_dumper", root_build_dir),
"-i",
rebase_path("${target_gen_dir}/interfaces.json", root_build_dir),
"-r",
rebase_path(root_gen_dir, root_build_dir),
"-m",
rebase_path("$target_gen_dir/metadata", root_build_dir),
"-t",
rebase_path("${target_gen_dir}/testcase.h", root_build_dir),
"-d",
rebase_path("${target_gen_dir}/", root_gen_dir),
"-o",
rebase_path("${target_gen_dir}/testcase.h", root_build_dir),
"-c",
"-n",
"renderer_in_process_mojolpm_fuzzer",
"-f",
rebase_path(depfile, root_build_dir),
]
foreach(interface, context_browser_exposed_interfaces) {
args += [ interface[1] ]
}
args += [ "-p" ]
foreach(interface, process_browser_exposed_interfaces) {
args += [ interface[1] ]
}
outputs = [ "${target_gen_dir}/testcase.h" ]
outputs = [
"${target_gen_dir}/interfaces.json",
"${target_gen_dir}/testcase.h",
]
deps += _mojolpm_deps
}
in_process_renderer_mojolpm_generated_fuzzer(
"renderer_in_process_mojolpm_fuzzer") {
sources = [ "renderer_in_process_mojolpm_fuzzer.cc" ]
interfaces = context_browser_exposed_interfaces
interfaces += process_browser_exposed_interfaces
_interfaces_deps = [
"//chrome/browser/lens/core/mojom:mojo_bindings_mojolpm",
"//chrome/browser/media:mojo_bindings_mojolpm",
"//chrome/browser/new_tab_page/modules/file_suggestion:mojo_bindings_mojolpm",
"//chrome/browser/new_tab_page/modules/photos:mojo_bindings_mojolpm",
"//chrome/browser/new_tab_page/modules/v2/history_clusters:mojo_bindings_mojolpm",
"//chrome/browser/new_tab_page/modules/v2/tab_resumption:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/access_code_cast:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/app_home:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/app_service_internals:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/bluetooth_internals:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/connectors_internals:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/discards:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/downloads:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/internals/user_education:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/location_internals:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/new_tab_page:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/new_tab_page/foo:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/new_tab_page_third_party:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/omnibox:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/privacy_sandbox:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/reset_password:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/search_engine_choice:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/segmentation_internals:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/side_panel/bookmarks:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/side_panel/customize_chrome:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/side_panel/reading_list:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/suggest_internals:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/tab_search:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/tab_strip:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/usb_internals:mojo_bindings_mojolpm",
"//chrome/browser/ui/webui/web_app_internals:mojo_bindings_mojolpm",
"//chrome/common:mojo_bindings_mojolpm",
"//chrome/common/accessibility:mojo_bindings_mojolpm",
"//chrome/common/cart:mojo_bindings_mojolpm",
"//components/browsing_topics/mojom:mojo_bindings_mojolpm",
"//components/commerce/core/internals/mojom:mojo_bindings_mojolpm",
"//components/dom_distiller/content/common/mojom:mojom_mojolpm",
"//components/history_clusters/history_clusters_internals/webui:mojo_bindings_mojolpm",
"//components/metrics/public/mojom:call_stack_mojo_bindings_mojolpm",
"//components/metrics/public/mojom:single_sample_metrics_mojo_bindings_mojolpm",
"//components/network_hints/common:mojo_bindings_mojolpm",
"//components/no_state_prefetch/common:mojo_bindings_mojolpm",
"//components/optimization_guide/optimization_guide_internals/webui:mojo_bindings_mojolpm",
"//components/page_image_service/mojom:mojo_bindings_mojolpm",
"//components/performance_manager/public/mojom:mojom_mojolpm",
"//components/safe_browsing/content/common:interfaces_mojolpm",
"//components/site_engagement/core/mojom:mojo_bindings_mojolpm",
"//components/spellcheck/common:interfaces_mojolpm",
"//components/translate/content/common:common_mojolpm",
"//content/browser/attribution_reporting:internals_mojo_bindings_mojolpm",
"//content/browser/indexed_db:internals_mojo_bindings_mojolpm",
"//content/browser/private_aggregation:mojo_bindings_mojolpm",
"//content/browser/process_internals:mojo_bindings_mojolpm",
"//content/browser/tracing/trace_report:mojo_bindings_mojolpm",
"//content/browser/xr/webxr_internals/mojom:mojo_bindings_mojolpm",
"//content/common:mojo_bindings_mojolpm",
"//device/gamepad/public/mojom:mojom_mojolpm",
"//device/vr/public/mojom:vr_service_mojolpm",
"//extensions/common/api:mojom_mojolpm",
"//media/capture/mojom:image_capture_mojolpm",
"//media/capture/mojom:video_capture_mojolpm",
"//media/midi:mojo_mojolpm",
"//media/mojo/mojom:mojom_mojolpm",
"//media/mojo/mojom:remoting_mojolpm",
"//media/mojo/mojom:speech_recognition_mojolpm",
"//media/mojo/mojom:web_speech_recognition_mojolpm",
"//services/device/public/mojom:mojom_mojolpm",
"//services/image_annotation/public/mojom:mojom_mojolpm",
"//services/network/public/mojom:cookies_mojom_mojolpm",
"//services/network/public/mojom:mojom_mojolpm",
"//services/network/public/mojom:url_loader_base_mojolpm",
"//services/resource_coordinator/public/mojom:mojom_mojolpm",
"//services/screen_ai/public/mojom:mojom_mojolpm",
"//services/shape_detection/public/mojom:mojom_mojolpm",
"//services/viz/public/mojom:mojom_mojolpm",
"//storage/browser/quota:mojo_bindings_mojolpm",
"//third_party/blink/public/mojom:android_mojo_bindings_mojolpm",
"//third_party/blink/public/mojom:authenticator_test_mojo_bindings_mojolpm",
"//third_party/blink/public/mojom:embedded_frame_sink_mojo_bindings_mojolpm",
"//third_party/blink/public/mojom:mojom_core_mojolpm",
"//third_party/blink/public/mojom:mojom_modules_mojolpm",
"//third_party/blink/public/mojom:mojom_platform_mojolpm",
"//third_party/blink/public/mojom:web_bluetooth_mojo_bindings_mojolpm",
"//third_party/blink/public/mojom/gpu:gpu_mojolpm",
"//third_party/blink/public/mojom/origin_trial_state:origin_trial_state_mojolpm",
"//third_party/blink/public/mojom/quota:quota_mojolpm",
"//third_party/blink/public/mojom/usb:usb_mojolpm",
"//ui/webui/resources/cr_components/app_management:mojo_bindings_mojolpm",
"//ui/webui/resources/cr_components/certificate_manager:mojom_mojolpm",
"//ui/webui/resources/cr_components/color_change_listener:mojom_mojolpm",
"//ui/webui/resources/cr_components/commerce:mojo_bindings_mojolpm",
"//ui/webui/resources/cr_components/customize_color_scheme_mode:mojom_mojolpm",
"//ui/webui/resources/cr_components/customize_themes:mojom_mojolpm",
"//ui/webui/resources/cr_components/help_bubble:mojo_bindings_mojolpm",
"//ui/webui/resources/cr_components/history_clusters:mojo_bindings_mojolpm",
"//ui/webui/resources/cr_components/most_visited:mojom_mojolpm",
"//ui/webui/resources/cr_components/searchbox:mojo_bindings_mojolpm",
"//ui/webui/resources/cr_components/theme_color_picker:mojom_mojolpm",
"//ui/webui/resources/js/browser_command:mojo_bindings_mojolpm",
"//ui/webui/resources/js/metrics_reporter:mojo_bindings_mojolpm",
]
interface_file = "${target_gen_dir}/interfaces.json"
deps = [
":renderer_in_process_mojolpm_fuzzer_generator",
@ -193,8 +113,10 @@ if (!is_android && !dcheck_always_on && fuzzing_engine_supports_custom_main) {
"//testing/libfuzzer/proto:url_proto_converter",
"//third_party/blink/public/common:storage_key_proto_converter",
]
deps += _interfaces_deps
proto_deps = _interfaces_deps
deps += _mojolpm_deps
proto_deps = [ ":renderer_in_process_mojolpm_fuzzer_generator" ]
proto_deps += _mojolpm_deps
}
}

@ -7,7 +7,40 @@
This script must be used with MojoLPMGenerator and a RendererFuzzer. It
generates code to handle interface creation requested by MojoLPM by using the
interface brokers provided by the internal RendererFuzzer mechanism.
`ipc_interfaces_dumper` binary. It then formats a JSON file that
MojoLPMGenerator will understand.
JSON Format:
{
# Those are the context (frame, document...) bound interfaces.
"context_interfaces": [
[
"//path/to/mojom.mojom",
"qualified.interface.name",
"{Associated,}Remote",
],
...
],
# Those are the process bound interfaces.
"process_interfaces": [
{
"//path/to/mojom.mojom",
"qualified.interface.name",
"{Associated,}Remote",
},
...
],
# Those are the format MojoLPMGenerator undertands.
# This groups all interfaces together.
"interfaces": [
{
"gen/path/to/generator/file.mojom-module",
"qualified.interface.name",
"{Associated,}Remote",
},
...
]
}
This script uses the jinja2 and the `testcase.h.tmpl` template to generate C++
code. A class named `RendererTestcase` will be created.
@ -15,45 +48,270 @@ code. A class named `RendererTestcase` will be created.
from __future__ import annotations
import abc
import argparse
import dataclasses
import json
import os
import pathlib
import re
import subprocess
import sys
import tempfile
import typing
import enum
# Copied from //mojo/public/tools/mojom/mojom/fileutil.py.
def AddLocalRepoThirdPartyDirToModulePath():
"""Helper function to find the top-level directory of this script's repository
assuming the script falls somewhere within a 'chrome' directory, and insert
the top-level 'third_party' directory early in the module search path. Used to
ensure that third-party dependencies provided within the repository itself
(e.g. Chromium sources include snapshots of jinja2 and ply) are preferred over
locally installed system library packages."""
def _GetDirAbove(dirname: str):
"""Returns the directory "above" this file containing |dirname| (which must
also be "above" this file)."""
path = os.path.abspath(__file__)
while True:
path, tail = os.path.split(path)
if not tail:
return None
if tail == dirname:
return path
toplevel_dir = _GetDirAbove('chrome')
if toplevel_dir:
sys.path.insert(1, os.path.join(toplevel_dir, 'third_party'))
def _GetDirAbove(dirname: str):
"""Returns the directory "above" this file containing |dirname| (which must
also be "above" this file)."""
path = os.path.abspath(__file__)
while True:
path, tail = os.path.split(path)
if not tail:
return None
if tail == dirname:
return path
# This is needed in order to be able to import jinja2.
AddLocalRepoThirdPartyDirToModulePath()
SOURCE_DIR = _GetDirAbove('chrome')
sys.path.insert(1, os.path.join(SOURCE_DIR, 'third_party'))
sys.path.append(os.path.join(SOURCE_DIR, 'build'))
sys.path.append(os.path.join(SOURCE_DIR, 'mojo/public/tools/mojom/'))
from mojom.parse import parser as mojom_parser, ast
import action_helpers
import jinja2
XVFB_PATH = os.path.join(SOURCE_DIR, 'testing/xvfb.py')
def strip_end(text: str, suffix: str) -> str:
"""Similar to python 3.9 `removesuffix` function.
Args:
text: input text.
suffix: the suffix to remove if present.
Returns:
the input string with suffix removed if present.
"""
if suffix and text.endswith(suffix):
return text[:-len(suffix)]
return text
def get_all_interfaces(metadata_file: str) -> typing.List[str]:
"""Returns the list of every mojo interfaces in src_dir. Recurses through
subdirectories.
Args:
src_dir: the chromium source directory.
Returns:
the list of paths to mojom interfaces.
"""
res = []
with open(metadata_file, 'r', encoding='utf-8') as file:
lines = [line.rstrip() for line in file]
for line in lines:
with open(line, 'r') as metadata:
data = json.load(metadata)
for mojom_file in data['sources']:
if mojom_file.endswith('.mojom'):
path = os.path.join(os.path.dirname(line), mojom_file)
path = os.path.abspath(path)
res.append(path)
return res
def is_defined_in_module(qualified_name: str, interface: ast.Mojom) -> bool:
namespace = ".".join(qualified_name.split('.')[:-1])
name = qualified_name.split('.')[-1]
if not interface.module:
return False
m_namespace = str(interface.module.mojom_namespace)
if m_namespace != namespace:
return False
if not interface.definition_list:
return False
for definition in interface.definition_list:
if (isinstance(definition, ast.Interface) and
str(definition.mojom_name) == name):
return True
return False
def find_matching_interface(qualified_name: str,
modules: typing.List[ast.Mojom]) -> str:
"""Finds the correct mojom file for the given interface. The interface name
must be qualified.
Args:
qualified_name: the qualified interface name (e.g.
'blink.mojom.BlobRegistry').
modules: the list of parsed mojom modules.
Returns:
the path to the mojom file corresponding to the input interface.
"""
for module in modules:
if is_defined_in_module(qualified_name, module):
return module.module.filename
return None
def ensure_interface_deps_complete(interfaces: typing.List[str],
modules: typing.List[ast.Mojom],
build_dir: str):
"""Ensures that all the interfaces can be fetched from the parsed mojom
modules.
Args:
interfaces: the list of interfaces (qualified names).
modules: the list of mojom modules to search into.
Raises:
Exception: if at least one interface could not be found.
"""
missing_interfaces = []
for interface in interfaces:
res = find_matching_interface(interface, modules)
if not res:
missing_interfaces.append(interface)
if len(missing_interfaces) != 0:
raise Exception('Missing browser exposed targets for the following '
'interfaces:\n'
f'{missing_interfaces}\n'
'Please add the corresponding targets to '
'`//chrome/browser_exposed_mojom_targets.gni`.')
def handle_interfaces(interfaces,
mojom_files: typing.List[ast.Mojom],
source_path: str,
output):
"""Finds the mojom files for the given interfaces and append the formatted
result to the output list.
Args:
interfaces: the interfaces to handle.
mojom_files: the list of parsed mojom files to look into.
source_path: the path to chromium's root source directory.
output: the output list.
"""
for interface in interfaces:
qualified_name = interface['qualified_name']
interface_type = interface['type']
path = pathlib.Path(find_matching_interface(qualified_name, mojom_files))
path = path.relative_to(source_path)
output.append([
f"//{path}", qualified_name, interface_type
])
def filter_data(data):
"""Filters the JSON data. As for now, we filter out:
- AssociatedRemote
- Duplicate interfaces for context and process interfaces.
Args:
data: the JSON data.
"""
data_filter = lambda x : x['type'] != 'AssociatedRemote'
data['context_interfaces'] = list(filter(data_filter,
data['context_interfaces']))
data['process_interfaces'] = list(filter(data_filter,
data['process_interfaces']))
ctx_interfaces = [s['qualified_name'] for s in data['context_interfaces']]
data_filter = lambda x: x['qualified_name'] not in ctx_interfaces
data['process_interfaces'] = list(filter(data_filter,
data['process_interfaces']))
def run_ipc_dumper(dumper_path: str, out_file: str):
"""This runs the ipc_dumper executable at `dumper_path` and redirects its
output to `out_file` so that this tool can use it to generate the list of
interfaces.
Args:
dumper_path: path to the `ipc_interfaces_dumper` executable.
out_file: the file to which we'll dump the interfaces.
"""
env = os.environ.copy()
env["IPC_DUMP_PATH"] = out_file
# Since we're running these at compile time, we need to make sure this will
# run regardless of the building flags being used.
# When enabling ASAN, we have a `detect_ord_violation` issue when running
# this tool.
env["ASAN_OPTIONS"] = 'detect_odr_violation=0'
args = [XVFB_PATH, os.path.abspath(dumper_path)]
subprocess.run(args, capture_output=True, env=env, check=True)
def generate_interfaces(ipc_interfaces_dumper: str,
interfaces_f: str,
gen_dir: str,
metadata_file: str,
depfile: str):
"""Generates the appropriate interfaces file given the output of the
`ipc_interfaces_dumper`.
Args:
ipc_interfaces_dumper: the path to the `ipc_interfaces_dumper` binary.
interfaces_f: the output path to the JSON interfaces file.
gen_dir: the path to the root gen directory.
metadata_file: the path to the mojo metadata file.
depfile: the depfile to write to.
"""
interfaces = get_all_interfaces(metadata_file)
parsed_interfaces = []
for interface in interfaces:
with open(interface, 'r', encoding="utf-8") as f:
parsed_interfaces.append(mojom_parser.Parse(f.read(), interface))
output = {"context_interfaces": [], "process_interfaces": []}
with tempfile.NamedTemporaryFile() as input_file:
run_ipc_dumper(ipc_interfaces_dumper, input_file.name)
with open(input_file.name, 'r') as in_f:
data = json.load(in_f)
filter_data(data)
all_interfaces = data['context_interfaces'] + data['process_interfaces']
qualified_names = [e['qualified_name'] for e in all_interfaces]
ensure_interface_deps_complete(qualified_names,
parsed_interfaces,
os.path.join(gen_dir, os.pardir))
handle_interfaces(data['context_interfaces'],
parsed_interfaces,
SOURCE_DIR,
output['context_interfaces'])
handle_interfaces(data['process_interfaces'],
parsed_interfaces,
SOURCE_DIR,
output['process_interfaces'])
# MojoLPMGenerator expects a particular format for generating MojoLPM
# boilerplate. This part will generate the expected format and rebase the
# mojom module paths in order for MojoLPMGenerator to be able to find them.
output['interfaces'] = []
for interface in output['context_interfaces'] + output['process_interfaces']:
path = interface[0]
path = os.path.join(gen_dir, path.lstrip('/')) + '-module'
output['interfaces'].append([
path, interface[1], interface[2]
])
with action_helpers.atomic_output(interfaces_f, mode="w") as f:
json.dump(output, f)
# Now, we want to write the depfile so that ninja knows that we're depending
# on the mojom files. If one gets modified, we want to re-run this action.
all_interfaces = output['context_interfaces'] + output['process_interfaces']
paths = [i[0].lstrip('//') for i in all_interfaces]
paths = [pathlib.Path(os.path.join(SOURCE_DIR, p)) for p in paths]
action_helpers.write_depfile(depfile,
interfaces_f,
[os.path.relpath(p) for p in paths])
def split_interface_name(interface: str):
"""Helper that splits a qualified mojo interface name into a dictionary
containing the key 'name' that contains the name of the interface, and the
@ -106,23 +364,50 @@ def camel_to_snake_case(name: str) -> str:
# character but only add the '_'.
return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
def generate_testcase(interfaces_f: str,
fuzzer_dir: str,
fuzzer_name: str,
testcase_f: str):
"""Generates the testcase file given the interface list and the
MojoLPMGenerator fuzzer name.
Args:
interfaces_f: the path to the JSON interface file.
fuzzer_dir: the path to the fuzzer directory.
fuzzer_name: the name of the MojoLPM fuzzer.
testcase_f: the output path to the testcase .h file.
"""
template_dir = os.path.dirname(os.path.abspath(__file__))
environment = jinja2.Environment(loader=jinja2.FileSystemLoader(
template_dir))
template = environment.get_template('testcase.h.tmpl')
fuzzer_path = os.path.join(fuzzer_dir, fuzzer_name)
fuzzer_name = snake_to_camel_case(fuzzer_name)
mojolpm_classname = f"mojolpmgenerator::{fuzzer_name}Testcase"
with open(interfaces_f, 'r') as f:
data = json.load(f)
context = [c[1] for c in data['context_interfaces']]
process = [p[1] for p in data['process_interfaces']]
context = {
"filename": testcase_f,
"mojolpm_generator_filepath": f"{fuzzer_path}.h",
"mojolpm_generator_classname": mojolpm_classname,
"process_interfaces": [split_interface_name(p) for p in process],
"context_interfaces": [split_interface_name(c) for c in context],
}
with action_helpers.atomic_output(testcase_f, mode="w") as f:
f.write(template.render(context))
def main():
parser = argparse.ArgumentParser(
description='Generate an IPC fuzzer based on MojoLPM Generator.')
parser.add_argument(
'-c',
'--context',
default=[],
nargs='+',
required=True,
help="Context bound interfaces to fuzz.")
parser.add_argument(
'-p',
'--process',
default=[],
nargs='+',
'--path',
required=True,
help="Process bound interfaces to fuzz.")
help="The path to ipc_interfaces_dumper binary.")
parser.add_argument(
'-d',
'--fuzzer_dir',
@ -135,28 +420,42 @@ def main():
help="""The name of the MojoLPMGenerator fuzzing target.
This will used to deduce the name of the generated MojoLPM testcase.""")
parser.add_argument(
'-o',
'--output',
'-t',
'--testcase-output-path',
required=True,
help="Output file name.")
help="The path where the testcase file will be written to.")
parser.add_argument(
'-i',
'--interface-output-path',
required=True,
help="The path where the interface file will be written to.")
parser.add_argument(
'-r',
'--root-gen-dir',
required=True,
help="The path to the root gen dir.")
parser.add_argument(
'-m',
'--metadata-file',
required=True,
help="Path to the metadata file.")
parser.add_argument(
'-f',
'--depfile',
required=True,
help="The path to the depfile.")
args = parser.parse_args()
template_dir = os.path.dirname(os.path.abspath(__file__))
environment = jinja2.Environment(loader=jinja2.FileSystemLoader(
template_dir))
template = environment.get_template('testcase.h.tmpl')
fuzzer_path = os.path.join(args.fuzzer_dir, args.name)
fuzzer_name = snake_to_camel_case(args.name)
mojolpm_classname = f"mojolpmgenerator::{fuzzer_name}Testcase"
context = {
"filename": args.output,
"mojolpm_generator_filepath": f"{fuzzer_path}.h",
"mojolpm_generator_classname": mojolpm_classname,
"process_interfaces": [split_interface_name(p) for p in args.process],
"context_interfaces": [split_interface_name(c) for c in args.context],
}
with pathlib.Path(args.output).open(mode="w") as f:
f.write(template.render(context))
generate_interfaces(args.path,
args.interface_output_path,
args.root_gen_dir,
args.metadata_file,
args.depfile)
generate_testcase(args.interface_output_path,
args.fuzzer_dir,
args.name,
args.testcase_output_path)
if __name__ == "__main__":
main()

@ -14,12 +14,14 @@
#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/fuzzing/in_process_fuzzer.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_test.h"
// Format for the outputted JSON:
// {
@ -41,19 +43,6 @@
// ]
// }
class IPCInterfaceDumper : public InProcessFuzzer {
public:
IPCInterfaceDumper() = default;
void RunTestOnMainThread() override;
int Fuzz(const uint8_t* data, size_t size) override { return 0; }
};
// We register this tool as an in_process_fuzzer so that it has the exact same
// setup as an in_process_fuzzer. Indeed, this is important because this can
// have an effect on which interfaces are being exposed.
REGISTER_IN_PROCESS_FUZZER(IPCInterfaceDumper)
namespace {
void RegisterInterfaces(const std::vector<std::string>& interfaces,
@ -72,7 +61,12 @@ void RegisterInterfaces(const std::vector<std::string>& interfaces,
} // namespace
void IPCInterfaceDumper::RunTestOnMainThread() {
class IPCInterfacesDumper : public InProcessBrowserTest {
public:
IPCInterfacesDumper() = default;
};
IN_PROC_BROWSER_TEST_F(IPCInterfacesDumper, DumperTest) {
auto env = base::Environment::Create();
if (!env->HasVar("IPC_DUMP_PATH")) {
LOG(ERROR) << "IPC_DUMP_PATH not set. Nothing will be done.";
@ -107,6 +101,8 @@ void IPCInterfaceDumper::RunTestOnMainThread() {
// Write the JSON to a file in the IPC_DUMP_PATH directory.
std::string file_path;
env->GetVar("IPC_DUMP_PATH", &file_path);
base::ScopedAllowBlockingForTesting allow_blocking;
#if BUILDFLAG(IS_WIN)
base::FilePath filepath = base::FilePath(base::UTF8ToWide(file_path));
#else

File diff suppressed because it is too large Load Diff

@ -196,6 +196,17 @@ template("mojolpm_fuzzer_test") {
# lists.
# The remote type must be either "Remote" or "AssociatedRemote".
#
# interface_file
# JSON file containing the list of interfaces to generate.
# Format:
# {
# "interfaces": {
# ["InterfaceName", "//path/to/interface.mojom", "Remote"],
# ["AnotherInterface", "//path/to/another.mojom", "AssociatedRemote" ],
# ...
# }
# }
#
# deps
# List of dependencies to compile this target.
#
@ -236,19 +247,22 @@ template("mojolpm_fuzzer_test") {
template("mojolpm_generated_fuzzer") {
assert(defined(invoker.sources),
"\"sources\" must be defined for $target_name")
assert(defined(invoker.interfaces),
"\"interfaces\" must be defined for $target_name")
assert(
defined(invoker.interfaces) || defined(invoker.interface_file),
"\"interfaces\" or \"interface_file\" must be defined for $target_name")
if (enable_mojom_fuzzer) {
_target_name = target_name
_generate_target_name = _target_name + "_mojolpm_generator_generate"
# Generates the correct argument format give invoker.interfaces.
_script_inputs = []
foreach(elt, invoker.interfaces) {
_script_inputs +=
[ rebase_path("$root_gen_dir/" + elt[0] + "-module", root_build_dir) +
":" + elt[1] + ":" + elt[2] ]
if (defined(invoker.interfaces)) {
# Generates the correct argument format give invoker.interfaces.
_script_inputs = []
foreach(elt, invoker.interfaces) {
_script_inputs +=
[ rebase_path("$root_gen_dir/" + elt[0] + "-module",
root_build_dir) + ":" + elt[1] + ":" + elt[2] ]
}
}
action(_generate_target_name) {
@ -261,8 +275,16 @@ template("mojolpm_generated_fuzzer") {
deps += invoker.proto_deps
}
args = [ "--input" ]
args += _script_inputs
args = []
if (defined(invoker.interfaces)) {
args += [ "--input" ]
args += _script_inputs
} else if (defined(invoker.interface_file)) {
args += [
"-f",
rebase_path(invoker.interface_file, root_build_dir),
]
}
args += [
"--output_file_format",
rebase_path("${target_gen_dir}/${_target_name}", root_build_dir),

@ -45,6 +45,7 @@ from __future__ import annotations
import abc
import argparse
import dataclasses
import json
import os
import pathlib
import re
@ -62,6 +63,7 @@ from mojom import fileutil
from mojom.generate import module
fileutil.AddLocalRepoThirdPartyDirToModulePath()
CHROME_SRC_DIR = fileutil._GetDirAbove('mojo')
import jinja2
@ -654,17 +656,48 @@ def build(interface: module.Interface,
return actions
def get_interface_list_from_file(
file_path: str) -> typing.List[typing.List[str]]:
"""Reads the JSON input file and returns the interfaces list that it
contains.
Args:
file_path: the path to the input file.
Returns:
the list of interfaces.
"""
with open(file_path, 'r') as f:
data = json.load(f)
return data['interfaces']
def get_interface_list_from_input(
interfaces: typing.List[str]) -> typing.List[typing.List[str]]:
"""Parses the input list of interfaces and returns a list of list that
matches the expected format.
Args:
interfaces: the list of strings listing the interfaces.
Returns:
the list of interfaces.
"""
return [interface.split(':') for interface in interfaces]
def main():
parser = argparse.ArgumentParser(
description='Generate MojoLPM proto and cpp/h files.')
parser.add_argument(
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
'-i',
'--input',
default=[],
nargs='+',
required=True,
help="input(s) with format: "
"path/to/interface.mojom-module:InterfaceName:{Remote|AssociatedRemote}")
group.add_argument('-f', '--file', help="")
parser.add_argument('--output_file_format',
required=True,
help="output file format. Files with extensions '.h' and"
@ -677,11 +710,11 @@ def main():
[MojoLPMProtoGenerator(output_file),
MojoLPMCppGenerator(output_file)])
actions = MojoLPMActionSet()
for file_interface in args.input:
custom_format = file_interface.split(':')
if len(custom_format) != 3:
print(f"Wrong format: {file_interface}. See help for usage.")
return
if args.file:
interfaces = get_interface_list_from_file(args.file)
else:
interfaces = get_interface_list_from_input(args.input)
for custom_format in interfaces:
(file, interface_name, remote_type_str) = custom_format
if remote_type_str == 'Remote':
remote_type = MojoLPMActionType.REMOTE_ACTION