blob: 00711c421166817f688d1a65b23507128c7782de [file] [log] [blame]
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +00001# Copyright 2022 The ChromiumOS Authors
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +00002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Run fuzzers on ChromeOS Devices for given packages.
6
7Given a remote host and a list of portage packages,
8run the associated package fuzzers in a sysroot on the
9remote host.
10
11As of 2022-10-24, this script is intended to
12only test fuzzers on x86_64 devices.
13
14Example:
15 cros_on_device_fuzz setup 'myhost:1234' 'my_package'
16 cros_on_device_fuzz fuzz 'myhost:1234' 'my_installed_fuzzer'
17"""
18
19import logging
20from pathlib import Path
21import re
22import tempfile
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000023from typing import Iterable, Optional
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000024
25from chromite.lib import commandline
26from chromite.lib import cros_build_lib
27from chromite.lib import on_device_fuzz
28from chromite.lib import remote_access
29
30
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000031_DUT_FUZZER_ROOT = Path("/usr/local/tmp/fuzzer_root")
32_IMPLICIT_SYSTEM_PACKAGE = "virtual/implicit-system"
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000033_PACKAGE_SEPARATOR = re.compile(r"[ ,]+")
34_FUZZER_BOARD = "amd64-generic"
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000035
36
37def main(argv: Iterable[str]):
38 """Dispatch to other functions."""
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000039 opts = parse_args(argv)
40 opts.func(opts)
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000041
42
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000043def setup_main(opts):
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000044 """Setup up the DUT for fuzzing."""
45 cros_build_lib.AssertInsideChroot()
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000046 device = _get_device(opts.device, opts.private_key)
47 # Get a nice string representation of the device host for logging
48 host = _format_collection(opts.device)
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000049 # We do this version check to ensure we have a CrOS device
50 # immediately.
51 try:
52 version = device.version
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000053 if not version:
54 raise RuntimeError("Version is null or empty.")
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000055 except Exception:
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000056 logging.error("Unable to get version of remote %s", host)
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000057 raise
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000058
59 logging.info("Connected to %s; CrOS version %s", host, version)
60 packages = set(re.split(_PACKAGE_SEPARATOR, opts.packages))
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000061 # We require virtual/implicit-system so that we can
62 # build the on-device sysroot. Without it, we'll
63 # miss critical libs and binaries.
64 packages.add(_IMPLICIT_SYSTEM_PACKAGE)
65 with tempfile.TemporaryDirectory() as tmpdir:
66 tmpdir = Path(tmpdir)
67 tarball_name = "sysroot_fuzzer_tarball.tar.xz"
68 on_device_fuzz.create_sysroot_tarball(
69 packages=packages,
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000070 board=_FUZZER_BOARD,
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000071 output_path=tmpdir / tarball_name,
72 )
73 on_device_fuzz.create_dut_sysroot(
74 device,
75 sysroot_tarball=tmpdir / tarball_name,
76 sysroot_device_path=_DUT_FUZZER_ROOT,
77 )
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000078 logging.info("Fuzzer set up complete for %s", host)
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000079
80
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000081def fuzz_main(opts):
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000082 """Run a given fuzzer on the DUT."""
83 cros_build_lib.AssertInsideChroot()
84 fuzzer_install_path = Path("/usr/libexec/fuzzers")
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000085 device = _get_device(opts.device, opts.private_key)
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000086 on_device_fuzz.run_fuzzer_executable(
87 device,
88 sysroot_device_path=_DUT_FUZZER_ROOT,
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000089 sysroot_fuzzer_path=fuzzer_install_path / opts.fuzzer_name,
90 libfuzzer_options={},
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000091 )
92
93
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000094def _get_device(
95 collection: commandline.Device, private_key: Optional[Path]
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000096) -> remote_access.ChromiumOSDevice:
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000097 """Get ChromiumOSDevice host from commandline.Device collection.
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +000098
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +000099 The device will always execute commands as root.
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000100
101 Args:
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +0000102 collection: Device collection object to connect to.
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000103 private_key: Path to private key to log into the device for.
104
105 Returns:
106 A remote_access.ChromiumOSDevice connection.
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000107 """
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +0000108 return remote_access.ChromiumOSDevice(
109 collection.hostname,
110 port=collection.port,
111 username="root",
112 private_key=private_key,
113 base_dir=_DUT_FUZZER_ROOT,
114 connect=True,
115 )
116
117
118def _format_collection(collection: commandline.Device) -> str:
119 if collection.port is None:
120 return collection.hostname
121 return f"{collection.hostname}:{collection.port}"
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000122
123
124def parse_args(raw_args: Iterable[str]):
125 """Parse CLI arguments."""
126 parser = commandline.ArgumentParser(description=__doc__)
127 parser.add_argument(
128 "--private-key",
129 type=Path,
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +0000130 default=None,
131 help="RSA key for device. (default: lib.remote_access's key path)",
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000132 )
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000133
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +0000134 # Device parsing aids.
135 device_parser = commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH)
136 device_host_help = "Device host, in the format '[ssh://]hostname[:port]'"
137
138 subparsers = parser.add_subparsers()
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000139 # setup subcommand
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +0000140 subparser = subparsers.add_parser(
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000141 "setup", help="Installs fuzzers on device sysroot."
142 )
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +0000143 subparser.set_defaults(func=setup_main)
144 subparser.add_argument(
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000145 "--device-sysroot-path",
146 type=Path,
147 help="Absolute location on device for the"
148 f" fuzzer sysroot. (default: {_DUT_FUZZER_ROOT})",
149 default=_DUT_FUZZER_ROOT,
150 )
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +0000151 subparser.add_argument("device", type=device_parser, help=device_host_help)
152 subparser.add_argument(
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000153 "packages",
154 help="Portage packages to install fuzzers for."
155 " Space or comma separated."
156 f" Automatically includes {_IMPLICIT_SYSTEM_PACKAGE}",
157 )
158
159 # fuzz subcommand
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +0000160 subparser = subparsers.add_parser(
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000161 "fuzz", help="Runs a fuzzer in device sysroot."
162 )
Jordan R Abrahams-Whiteheada06da4d2022-12-06 01:25:55 +0000163 subparser.set_defaults(func=fuzz_main)
164 subparser.add_argument("device", type=device_parser, help=device_host_help)
165 subparser.add_argument(
Jordan R Abrahams-Whitehead315df5e2022-10-24 21:55:37 +0000166 "fuzzer_name", help="Name of the fuzzer executable to run."
167 )
168
169 return parser.parse_args(raw_args)