diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-03 13:42:47 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:27:51 +0000 |
commit | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (patch) | |
tree | d29d987c4d7b173cf853279b79a51598f104b403 /chromium/third_party/crashpad | |
parent | 830c9e163d31a9180fadca926b3e1d7dfffb5021 (diff) | |
download | qtwebengine-chromium-8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec.tar.gz |
BASELINE: Update Chromium to 66.0.3359.156
Change-Id: I0c9831ad39911a086b6377b16f995ad75a51e441
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/crashpad')
110 files changed, 5311 insertions, 626 deletions
diff --git a/chromium/third_party/crashpad/README.chromium b/chromium/third_party/crashpad/README.chromium index cb7b8cca5e6..3101da52699 100644 --- a/chromium/third_party/crashpad/README.chromium +++ b/chromium/third_party/crashpad/README.chromium @@ -2,7 +2,7 @@ Name: Crashpad Short Name: crashpad URL: https://crashpad.chromium.org/ Version: unknown -Revision: e9d2ca60f7329bf86951a6c9704953303ecaf6a6 +Revision: a8ecdbc973d969a87aaa2efffb1668efb52b799d License: Apache 2.0 License File: crashpad/LICENSE Security Critical: yes @@ -36,3 +36,4 @@ $ git am --3way --message-id -p4 /tmp/patchdir Local Modifications: - codereview.settings has been excluded. + - thread_log_messages.cc (using ThreadLocalStorage::Slot instead of StaticSlot) diff --git a/chromium/third_party/crashpad/crashpad/AUTHORS b/chromium/third_party/crashpad/crashpad/AUTHORS index b1e4ddf9686..eb3534a6f8d 100644 --- a/chromium/third_party/crashpad/crashpad/AUTHORS +++ b/chromium/third_party/crashpad/crashpad/AUTHORS @@ -8,3 +8,4 @@ Google Inc. Opera Software ASA +Vewd Software AS diff --git a/chromium/third_party/crashpad/crashpad/BUILD.gn b/chromium/third_party/crashpad/crashpad/BUILD.gn index 9f386afa478..ffd1c5763a4 100644 --- a/chromium/third_party/crashpad/crashpad/BUILD.gn +++ b/chromium/third_party/crashpad/crashpad/BUILD.gn @@ -19,7 +19,7 @@ config("crashpad_config") { include_dirs = [ "." ] } -if (crashpad_is_in_chromium) { +if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { test("crashpad_tests") { deps = [ "client:client_test", @@ -31,7 +31,25 @@ if (crashpad_is_in_chromium) { "util:util_test", ] } -} else { + + if (crashpad_is_in_fuchsia) { + import("//build/package.gni") + package("crashpad_test") { + testonly = true + system_image = true + + deps = [ + ":crashpad_tests", + ] + + tests = [ + { + name = "crashpad_tests" + }, + ] + } + } +} else if (crashpad_is_standalone) { test("crashpad_client_test") { deps = [ "client:client_test", diff --git a/chromium/third_party/crashpad/crashpad/DEPS b/chromium/third_party/crashpad/crashpad/DEPS index b197e6565df..6936f9f2014 100644 --- a/chromium/third_party/crashpad/crashpad/DEPS +++ b/chromium/third_party/crashpad/crashpad/DEPS @@ -19,7 +19,7 @@ vars = { deps = { 'buildtools': Var('chromium_git') + '/chromium/buildtools.git@' + - '505de88083136eefd056e5ee4ca0f01fe9b33de8', + '6fe4a3251488f7af86d64fc25cf442e817cf6133', 'crashpad/third_party/gtest/gtest': Var('chromium_git') + '/external/github.com/google/googletest@' + 'd175c8bf823e709d570772b038757fadf63bc632', @@ -28,7 +28,7 @@ deps = { '5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'edfe51ce818e55eae73ab3f144a360e855533888', + '3b953302848580cdf23b50402befc0ae09d03ff9', 'crashpad/third_party/zlib/zlib': Var('chromium_git') + '/chromium/src/third_party/zlib@' + '13dc246a58e4b72104d35f9b1809af95221ebda7', diff --git a/chromium/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni b/chromium/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni index 021bd53aefb..e9ac5713e90 100644 --- a/chromium/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni +++ b/chromium/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni @@ -17,6 +17,14 @@ declare_args() { # targets to use for dependencies. Valid values are "standalone", "chromium", # and "fuchsia". crashpad_dependencies = "standalone" + + if (defined(is_fuchsia_tree) && is_fuchsia_tree) { + # Determines various flavors of build configuration, and which concrete + # targets to use for dependencies. Valid values are "standalone", + # "chromium", and "fuchsia". Defaulted to "fuchsia" because + # "is_fuchsia_tree" is set. + crashpad_dependencies = "fuchsia" + } } assert( @@ -38,15 +46,10 @@ if (crashpad_is_in_chromium) { crashpad_is_clang = is_clang } else { - # Using mini_chromium, but in different locations depending on whether in - # Fuchsia, or standalone. - if (crashpad_is_in_fuchsia) { - import("//third_party/mini_chromium/build/compiler.gni") - import("//third_party/mini_chromium/build/platform.gni") - } else { - import("../third_party/mini_chromium/mini_chromium/build/compiler.gni") - import("../third_party/mini_chromium/mini_chromium/build/platform.gni") - } + # Both standalone and in Fuchsia tree use mini_chromium, and it's mapped into + # the same location in both cases. + import("../third_party/mini_chromium/mini_chromium/build/compiler.gni") + import("../third_party/mini_chromium/mini_chromium/build/platform.gni") crashpad_is_mac = mini_chromium_is_mac crashpad_is_win = mini_chromium_is_win crashpad_is_linux = mini_chromium_is_linux diff --git a/chromium/third_party/crashpad/crashpad/build/run_fuchsia_qemu.py b/chromium/third_party/crashpad/crashpad/build/run_fuchsia_qemu.py index e3f4efc3e1f..135b314ea3a 100755 --- a/chromium/third_party/crashpad/crashpad/build/run_fuchsia_qemu.py +++ b/chromium/third_party/crashpad/crashpad/build/run_fuchsia_qemu.py @@ -27,6 +27,7 @@ import signal import subprocess import sys import tempfile +import time try: from subprocess import DEVNULL @@ -57,7 +58,7 @@ def _CheckForTun(): if returncode != 0: print('To use QEMU with networking on Linux, configure TUN/TAP. See:', file=sys.stderr) - print(' https://fuchsia.googlesource.com/magenta/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only', + print(' https://fuchsia.googlesource.com/zircon/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only', file=sys.stderr) return 2 return 0 @@ -77,6 +78,8 @@ def _Start(pid_file): initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin') mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3)) + instance_name = 'crashpad_qemu_' + \ + ''.join(chr(random.randint(ord('A'), ord('Z'))) for x in range(8)) # These arguments are from the Fuchsia repo in zircon/scripts/run-zircon. popen = subprocess.Popen([ @@ -93,12 +96,24 @@ def _Start(pid_file): '-enable-kvm', '-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0', '-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail, - '-append', 'TERM=dumb', + '-append', 'TERM=dumb zircon.nodename=' + instance_name, ], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL) with open(pid_file, 'wb') as f: f.write('%d\n' % popen.pid) + for i in range(10): + netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools', 'netaddr') + if subprocess.call([netaddr_path, '--nowait', instance_name], + stdout=open(os.devnull), stderr=open(os.devnull)) == 0: + break + time.sleep(.5) + else: + print('instance did not respond after start', file=sys.stderr) + return 1 + + return 0 + def main(args): if len(args) != 1 or args[0] not in ('start', 'stop'): @@ -110,7 +125,7 @@ def main(args): pid_file = os.path.join(tempfile.gettempdir(), 'crashpad_fuchsia_qemu_pid') _Stop(pid_file) if command == 'start': - _Start(pid_file) + return _Start(pid_file) return 0 diff --git a/chromium/third_party/crashpad/crashpad/build/run_tests.py b/chromium/third_party/crashpad/crashpad/build/run_tests.py index e180a6cbac8..6c2dacf4dd7 100755 --- a/chromium/third_party/crashpad/crashpad/build/run_tests.py +++ b/chromium/third_party/crashpad/crashpad/build/run_tests.py @@ -17,6 +17,7 @@ from __future__ import print_function +import argparse import os import pipes import posixpath @@ -30,19 +31,54 @@ CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), IS_WINDOWS_HOST = sys.platform.startswith('win') +def _FindGNFromBinaryDir(binary_dir): + """Attempts to determine the path to a GN binary used to generate the build + files in the given binary_dir. This is necessary because `gn` might not be in + the path or might be in a non-standard location, particularly on build + machines.""" + + build_ninja = os.path.join(binary_dir, 'build.ninja') + if os.path.isfile(build_ninja): + with open(build_ninja, 'rb') as f: + # Look for the always-generated regeneration rule of the form: + # + # rule gn + # command = <gn binary> ... arguments ... + # + # to extract the gn binary's full path. + found_rule_gn = False + for line in f: + if line.strip() == 'rule gn': + found_rule_gn = True + continue + if found_rule_gn: + if len(line) == 0 or line[0] != ' ': + return None + if line.startswith(' command = '): + gn_command_line_parts = line.strip().split(' ') + if len(gn_command_line_parts) > 2: + return os.path.join(binary_dir, gn_command_line_parts[2]) + + return None + + def _BinaryDirTargetOS(binary_dir): """Returns the apparent target OS of binary_dir, or None if none appear to be explicitly specified.""" - # Look for a GN “target_os”. - popen = subprocess.Popen( - ['gn', 'args', binary_dir, '--list=target_os', '--short'], - shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull)) - value = popen.communicate()[0] - if popen.returncode == 0: - match = re.match('target_os = "(.*)"$', value.decode('utf-8')) - if match: - return match.group(1) + gn_path = _FindGNFromBinaryDir(binary_dir) + + if gn_path: + # Look for a GN “target_os”. + popen = subprocess.Popen( + [gn_path, 'args', binary_dir, '--list=target_os', '--short'], + shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull), + cwd=CRASHPAD_DIR) + value = popen.communicate()[0] + if popen.returncode == 0: + match = re.match('target_os = "(.*)"$', value.decode('utf-8')) + if match: + return match.group(1) # For GYP with Ninja, look for the appearance of “linux-android” in the path # to ar. This path is configured by gyp_crashpad_android.py. @@ -97,7 +133,7 @@ def _EnableVTProcessingOnWindowsConsole(): return True -def _RunOnAndroidTarget(binary_dir, test, android_device): +def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line): local_test_path = os.path.join(binary_dir, test) MAYBE_UNSUPPORTED_TESTS = ( 'crashpad_client_test', @@ -262,7 +298,7 @@ def _RunOnAndroidTarget(binary_dir, test, android_device): else: gtest_color = 'no' env['GTEST_COLOR'] = gtest_color - _adb_shell([posixpath.join(device_out_dir, test)], env) + _adb_shell([posixpath.join(device_out_dir, test)] + extra_command_line, env) finally: _adb_shell(['rm', '-rf', device_temp_dir]) @@ -277,8 +313,10 @@ def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests): targets_file = os.path.abspath(os.path.join(binary_dir, 'targets.txt')) with open(targets_file, 'wb') as f: f.write('//:' + '\n//:'.join(tests) + '\n') + gn_path = _FindGNFromBinaryDir(binary_dir) subprocess.check_call( - ['gn', 'gen', binary_dir, '--runtime-deps-list-file=' + targets_file]) + [gn_path, 'gen', binary_dir, '--runtime-deps-list-file=' + targets_file], + cwd=CRASHPAD_DIR) def _HandleOutputFromFuchsiaLogListener(process, done_message): @@ -300,7 +338,7 @@ def _HandleOutputFromFuchsiaLogListener(process, done_message): return success -def _RunOnFuchsiaTarget(binary_dir, test, device_name): +def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line): """Runs the given Fuchsia |test| executable on the given |device_name|. The device must already be booted. @@ -330,8 +368,9 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name): try: unique_id = uuid.uuid4().hex - tmp_root = '/tmp/%s_%s/tmp' % (test, unique_id) - staging_root = '/tmp/%s_%s/pkg' % (test, unique_id) + test_root = '/tmp/%s_%s' % (test, unique_id) + tmp_root = test_root + '/tmp' + staging_root = test_root + '/pkg' # Make a staging directory tree on the target. directories_to_create = [tmp_root, '%s/bin' % staging_root, @@ -341,11 +380,21 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name): def netcp(local_path): """Uses `netcp` to copy a file or directory to the device. Files located inside the build dir are stored to /pkg/bin, otherwise to /pkg/assets. + .so files are stored somewhere completely different, into /boot/lib (!). + This is because the loader service does not yet correctly handle the + namespace in which the caller is being run, and so can only load .so files + from a couple hardcoded locations, the only writable one of which is + /boot/lib, so we copy all .so files there. This bug is filed upstream as + ZX-1619. """ in_binary_dir = local_path.startswith(binary_dir + '/') if in_binary_dir: - target_path = os.path.join( - staging_root, 'bin', local_path[len(binary_dir)+1:]) + if local_path.endswith('.so'): + target_path = os.path.join( + '/boot/lib', local_path[len(binary_dir)+1:]) + else: + target_path = os.path.join( + staging_root, 'bin', local_path[len(binary_dir)+1:]) else: target_path = os.path.join(staging_root, 'assets', local_path) netcp_path = os.path.join(sdk_root, 'tools', 'netcp') @@ -365,8 +414,9 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name): done_message = 'TERMINATED: ' + unique_id namespace_command = [ - 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, '--', - staging_root + '/bin/' + test] + 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, + '--replace-child-argv0=/pkg/bin/' + test, '--', + staging_root + '/bin/' + test] + extra_command_line netruncmd(namespace_command, ['echo', done_message]) success = _HandleOutputFromFuchsiaLogListener( @@ -374,19 +424,19 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name): if not success: raise subprocess.CalledProcessError(1, test) finally: - netruncmd(['rm', '-rf', tmp_root, staging_root]) + netruncmd(['rm', '-rf', test_root]) # This script is primarily used from the waterfall so that the list of tests # that are run is maintained in-tree, rather than in a separate infrastructure # location in the recipe. def main(args): - if len(args) != 1 and len(args) != 2: - print('usage: run_tests.py <binary_dir> [test_to_run]', file=sys.stderr) - return 1 - - binary_dir = args[0] - single_test = args[1] if len(args) == 2 else None + parser = argparse.ArgumentParser(description='Run Crashpad unittests.') + parser.add_argument('binary_dir', help='Root of build dir') + parser.add_argument('test', nargs='*', help='Specific test(s) to run.') + parser.add_argument('--gtest_filter', + help='GTest filter applied to GTest binary runs.') + args = parser.parse_args() # Tell 64-bit Windows tests where to find 32-bit test executables, for # cross-bitted testing. This relies on the fact that the GYP build by default @@ -394,13 +444,13 @@ def main(args): # 64-bit build. This is not a universally valid assumption, and if it’s not # met, 64-bit tests that require 32-bit build output will disable themselves # dynamically. - if (sys.platform == 'win32' and binary_dir.endswith('_x64') and + if (sys.platform == 'win32' and args.binary_dir.endswith('_x64') and 'CRASHPAD_TEST_32_BIT_OUTPUT' not in os.environ): - binary_dir_32 = binary_dir[:-4] + binary_dir_32 = args.binary_dir[:-4] if os.path.isdir(binary_dir_32): os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32 - target_os = _BinaryDirTargetOS(binary_dir) + target_os = _BinaryDirTargetOS(args.binary_dir) is_android = target_os == 'android' is_fuchsia = target_os == 'fuchsia' @@ -445,15 +495,16 @@ def main(args): zircon_nodename = devices[0].strip().split()[1] print('Using autodetected Fuchsia device:', zircon_nodename) _GenerateFuchsiaRuntimeDepsFiles( - binary_dir, [t for t in tests if not t.endswith('.py')]) + args.binary_dir, [t for t in tests if not t.endswith('.py')]) elif IS_WINDOWS_HOST: tests.append('snapshot/win/end_to_end_test.py') - if single_test: - if single_test not in tests: - print('Unrecognized test:', single_test, file=sys.stderr) - return 3 - tests = [single_test] + if args.test: + for t in args.test: + if t not in tests: + print('Unrecognized test:', t, file=sys.stderr) + return 3 + tests = args.test for test in tests: print('-' * 80) @@ -461,14 +512,20 @@ def main(args): print('-' * 80) if test.endswith('.py'): subprocess.check_call( - [sys.executable, os.path.join(CRASHPAD_DIR, test), binary_dir]) + [sys.executable, os.path.join(CRASHPAD_DIR, test), args.binary_dir]) else: + extra_command_line = [] + if args.gtest_filter: + extra_command_line.append('--gtest_filter=' + args.gtest_filter) if is_android: - _RunOnAndroidTarget(binary_dir, test, android_device) + _RunOnAndroidTarget(args.binary_dir, test, android_device, + extra_command_line) elif is_fuchsia: - _RunOnFuchsiaTarget(binary_dir, test, zircon_nodename) + _RunOnFuchsiaTarget(args.binary_dir, test, zircon_nodename, + extra_command_line) else: - subprocess.check_call(os.path.join(binary_dir, test)) + subprocess.check_call([os.path.join(args.binary_dir, test)] + + extra_command_line) return 0 diff --git a/chromium/third_party/crashpad/crashpad/client/BUILD.gn b/chromium/third_party/crashpad/crashpad/client/BUILD.gn index f7fd288ffa4..f054e7caab8 100644 --- a/chromium/third_party/crashpad/crashpad/client/BUILD.gn +++ b/chromium/third_party/crashpad/crashpad/client/BUILD.gn @@ -45,6 +45,10 @@ static_library("client") { ] } + if (crashpad_is_linux || crashpad_is_android) { + sources += [ "crashpad_client_linux.cc" ] + } + if (crashpad_is_win) { sources += [ "crash_report_database_win.cc", diff --git a/chromium/third_party/crashpad/crashpad/client/client.gyp b/chromium/third_party/crashpad/crashpad/client/client.gyp index 4b14c4bf166..f75f9c40daa 100644 --- a/chromium/third_party/crashpad/crashpad/client/client.gyp +++ b/chromium/third_party/crashpad/crashpad/client/client.gyp @@ -40,6 +40,7 @@ 'crash_report_database_mac.mm', 'crash_report_database_win.cc', 'crashpad_client.h', + 'crashpad_client_linux.cc', 'crashpad_client_mac.cc', 'crashpad_client_win.cc', 'crashpad_info.cc', diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client.h b/chromium/third_party/crashpad/crashpad/client/crashpad_client.h index 7799bd9c9f1..7cf2eb8aa1f 100644 --- a/chromium/third_party/crashpad/crashpad/client/crashpad_client.h +++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client.h @@ -105,6 +105,71 @@ class CrashpadClient { bool restartable, bool asynchronous_start); +#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN + //! \brief Installs a signal handler to launch a handler process in reponse to + //! a crash. + //! + //! The handler process will create a crash dump for this process and exit. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandlerAtCrash( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments); + + //! \brief Starts a handler process with an initial client. + //! + //! This method allows a process to launch the handler process on behalf of + //! another process. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! \param[in] socket The server end of a socket pair. The client end should + //! be used with an ExceptionHandlerClient. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandlerForClient( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments, + int socket); +#endif // OS_LINUX || OS_ANDROID || DOXYGEN + #if defined(OS_MACOSX) || DOXYGEN //! \brief Sets the process’ crash handler to a Mach service registered with //! the bootstrap server. @@ -239,9 +304,9 @@ class CrashpadClient { //! used as the exception code in the exception record. //! //! \return `true` if the exception was triggered successfully. - bool DumpAndCrashTargetProcess(HANDLE process, - HANDLE blame_thread, - DWORD exception_code) const; + static bool DumpAndCrashTargetProcess(HANDLE process, + HANDLE blame_thread, + DWORD exception_code); enum : uint32_t { //! \brief The exception code (roughly "Client called") used when diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client_linux.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_client_linux.cc new file mode 100644 index 00000000000..a36d92297a8 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client_linux.cc @@ -0,0 +1,204 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "client/crashpad_client.h" + +#include <fcntl.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "util/file/file_io.h" +#include "util/linux/exception_handler_client.h" +#include "util/linux/exception_information.h" +#include "util/misc/from_pointer_cast.h" +#include "util/posix/double_fork_and_exec.h" +#include "util/posix/signals.h" + +namespace crashpad { + +namespace { + +std::string FormatArgumentString(const std::string& name, + const std::string& value) { + return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); +} + +std::string FormatArgumentInt(const std::string& name, int value) { + return base::StringPrintf("--%s=%d", name.c_str(), value); +} + +std::string FormatArgumentAddress(const std::string& name, void* addr) { + return base::StringPrintf("--%s=%p", name.c_str(), addr); +} + +void BuildHandlerArgvStrings( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments, + std::vector<std::string>* argv_strings) { + argv_strings->clear(); + + argv_strings->push_back(handler.value()); + for (const auto& argument : arguments) { + argv_strings->push_back(argument); + } + + if (!database.empty()) { + argv_strings->push_back(FormatArgumentString("database", database.value())); + } + + if (!metrics_dir.empty()) { + argv_strings->push_back( + FormatArgumentString("metrics-dir", metrics_dir.value())); + } + + if (!url.empty()) { + argv_strings->push_back(FormatArgumentString("url", url)); + } + + for (const auto& kv : annotations) { + argv_strings->push_back( + FormatArgumentString("annotation", kv.first + '=' + kv.second)); + } +} + +void ConvertArgvStrings(const std::vector<std::string> argv_strings, + std::vector<const char*>* argv) { + argv->clear(); + argv->reserve(argv_strings.size() + 1); + for (const auto& arg : argv_strings) { + argv->push_back(arg.c_str()); + } + argv->push_back(nullptr); +} + +// Launches a single use handler to snapshot this process. +class LaunchAtCrashHandler { + public: + static LaunchAtCrashHandler* Get() { + static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler(); + return instance; + } + + bool Initialize(std::vector<std::string>* argv_in) { + argv_strings_.swap(*argv_in); + + argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception", + &exception_information_)); + + ConvertArgvStrings(argv_strings_, &argv_); + return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); + } + + private: + LaunchAtCrashHandler() = default; + + ~LaunchAtCrashHandler() = delete; + + static void HandleCrash(int signo, siginfo_t* siginfo, void* context) { + auto state = Get(); + auto exception_information = &state->exception_information_; + + exception_information->siginfo_address = + FromPointerCast<decltype(exception_information->siginfo_address)>( + siginfo); + exception_information->context_address = + FromPointerCast<decltype(exception_information->context_address)>( + context); + exception_information->thread_id = syscall(SYS_gettid); + + pid_t pid = fork(); + if (pid < 0) { + return; + } + if (pid == 0) { + execv(state->argv_[0], const_cast<char* const*>(state->argv_.data())); + return; + } + + int status; + waitpid(pid, &status, 0); + } + + std::vector<std::string> argv_strings_; + std::vector<const char*> argv_; + ExceptionInformation exception_information_; + + DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler); +}; + +} // namespace + +CrashpadClient::CrashpadClient() {} + +CrashpadClient::~CrashpadClient() {} + +bool CrashpadClient::StartHandler( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments, + bool restartable, + bool asynchronous_start) { + // TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever + // supports accepting new connections. + // https://crashpad.chromium.org/bug/30 + NOTREACHED(); + return false; +} + +bool CrashpadClient::StartHandlerAtCrash( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments) { + std::vector<std::string> argv; + BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments, &argv); + + auto signal_handler = LaunchAtCrashHandler::Get(); + return signal_handler->Initialize(&argv); +} + +bool CrashpadClient::StartHandlerForClient( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map<std::string, std::string>& annotations, + const std::vector<std::string>& arguments, + int socket) { + std::vector<std::string> argv; + BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments, &argv); + + argv.push_back(FormatArgumentInt("initial-client", socket)); + + return DoubleForkAndExec(argv, socket, true, nullptr); +} + +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc index a16f8d28d57..4a408206e9f 100644 --- a/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc +++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc @@ -331,17 +331,6 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { } argv.push_back(FormatArgumentInt("handshake-fd", server_write_fd.get())); - // argv_c contains const char* pointers and is terminated by nullptr. argv - // is required because the pointers in argv_c need to point somewhere, and - // they can’t point to temporaries such as those returned by - // FormatArgumentString(). - std::vector<const char*> argv_c; - argv_c.reserve(argv.size() + 1); - for (const std::string& argument : argv) { - argv_c.push_back(argument.c_str()); - } - argv_c.push_back(nullptr); - // When restarting, reset the system default crash handler first. Otherwise, // the crash exception port in the handler will have been inherited from // this parent process, which was probably using the exception server now diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc index 7d9c1fb29c4..dd25c9ef9b4 100644 --- a/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc +++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc @@ -790,9 +790,10 @@ void CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) { UnhandledExceptionHandler(exception_pointers); } +// static bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process, HANDLE blame_thread, - DWORD exception_code) const { + DWORD exception_code) { // Confirm we're on Vista or later. const DWORD version = GetVersion(); const DWORD major_version = LOBYTE(LOWORD(version)); diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc index d2896f4a7ea..b545a3cd5c7 100644 --- a/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc +++ b/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc @@ -51,17 +51,24 @@ static_assert(std::is_standard_layout<CrashpadInfo>::value, // This may result in a static module initializer in debug-mode builds, but // because it’s POD, no code should need to run to initialize this under // release-mode optimization. + +// Platforms that use ELF objects need to locate this structure via the dynamic +// symbol table, so avoid name mangling. +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) +extern "C" { +#endif + #if defined(OS_POSIX) __attribute__(( +#if defined(OS_MACOSX) // Put the structure in a well-known section name where it can be easily // found without having to consult the symbol table. -#if defined(OS_MACOSX) section(SEG_DATA ",crashpad_info"), -#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) - section("crashpad_info"), -#else -#error Port + + // There's no need to expose this as a public symbol from the symbol table. + // All accesses from the outside can locate the well-known section name. + visibility("hidden"), #endif #if defined(ADDRESS_SANITIZER) @@ -74,11 +81,7 @@ __attribute__(( #endif // defined(ADDRESS_SANITIZER) // The “used” attribute prevents the structure from being dead-stripped. - used, - - // There’s no need to expose this as a public symbol from the symbol table. - // All accesses from the outside can locate the well-known section name. - visibility("hidden"))) + used)) #elif defined(OS_WIN) @@ -93,6 +96,10 @@ __declspec(allocate("CPADinfo")) CrashpadInfo g_crashpad_info; +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA) +} // extern "C" +#endif + // static CrashpadInfo* CrashpadInfo::GetCrashpadInfo() { return &g_crashpad_info; diff --git a/chromium/third_party/crashpad/crashpad/compat/BUILD.gn b/chromium/third_party/crashpad/crashpad/compat/BUILD.gn index 52eda78bc87..8db7b4dee22 100644 --- a/chromium/third_party/crashpad/crashpad/compat/BUILD.gn +++ b/chromium/third_party/crashpad/crashpad/compat/BUILD.gn @@ -21,10 +21,14 @@ config("compat_config") { include_dirs += [ "mac" ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { include_dirs += [ "linux" ] } + if (crashpad_is_android) { + include_dirs += [ "android" ] + } + if (crashpad_is_win) { include_dirs += [ "win" ] } else { @@ -61,13 +65,31 @@ compat_target("compat") { sources += [ "non_mac/mach/mach.h" ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/signal.h", "linux/sys/ptrace.h", ] } + if (crashpad_is_android) { + sources += [ + "android/dlfcn_internal.cc", + "android/dlfcn_internal.h", + "android/elf.h", + "android/linux/elf.h", + "android/linux/prctl.h", + "android/linux/ptrace.h", + "android/sched.h", + "android/sys/epoll.cc", + "android/sys/epoll.h", + "android/sys/mman.cc", + "android/sys/mman.h", + "android/sys/syscall.h", + "android/sys/user.h", + ] + } + if (crashpad_is_win) { sources += [ "win/getopt.h", diff --git a/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.cc b/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.cc new file mode 100644 index 00000000000..5d98fe8472a --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.cc @@ -0,0 +1,166 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dlfcn_internal.h" + +#include <android/api-level.h> +#include <dlfcn.h> +#include <errno.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <sys/syscall.h> +#include <sys/system_properties.h> +#include <unistd.h> + +#include <mutex> + +namespace crashpad { +namespace internal { + +// KitKat supports API levels up to 20. +#if __ANDROID_API__ < 21 + +namespace { + +class ScopedSigactionRestore { + public: + ScopedSigactionRestore() : old_action_(), signo_(-1), valid_(false) {} + + ~ScopedSigactionRestore() { Reset(); } + + bool Reset() { + bool result = true; + if (valid_) { + result = sigaction(signo_, &old_action_, nullptr) == 0; + if (!result) { + PrintErrmsg(errno); + } + } + valid_ = false; + signo_ = -1; + return result; + } + + bool ResetAndInstallHandler(int signo, + void (*handler)(int, siginfo_t*, void*)) { + Reset(); + + struct sigaction act; + act.sa_sigaction = handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + if (sigaction(signo, &act, &old_action_) != 0) { + PrintErrmsg(errno); + return false; + } + signo_ = signo; + valid_ = true; + return true; + } + + private: + void PrintErrmsg(int err) { + char errmsg[256]; + + if (strerror_r(err, errmsg, sizeof(errmsg)) != 0) { + snprintf(errmsg, + sizeof(errmsg), + "%s:%d: Couldn't set errmsg for %d: %d", + __FILE__, + __LINE__, + err, + errno); + return; + } + + fprintf(stderr, "%s:%d: sigaction: %s", __FILE__, __LINE__, errmsg); + } + + struct sigaction old_action_; + int signo_; + bool valid_; +}; + +bool IsKitKat() { + char prop_buf[PROP_VALUE_MAX]; + int length = __system_property_get("ro.build.version.sdk", prop_buf); + if (length <= 0) { + fprintf(stderr, "%s:%d: Couldn't get version", __FILE__, __LINE__); + // It's safer to assume this is KitKat and execute dlsym with a signal + // handler installed. + return true; + } + if (strcmp(prop_buf, "19") == 0 || strcmp(prop_buf, "20") == 0) { + return true; + } + return false; +} + +class ScopedSetTID { + public: + explicit ScopedSetTID(pid_t* tid) : tid_(tid) { *tid_ = syscall(SYS_gettid); } + + ~ScopedSetTID() { *tid_ = -1; } + + private: + pid_t* tid_; +}; + +sigjmp_buf dlsym_sigjmp_env; + +pid_t dlsym_tid = -1; + +void HandleSIGFPE(int signo, siginfo_t* siginfo, void* context) { + if (siginfo->si_code != FPE_INTDIV || syscall(SYS_gettid) != dlsym_tid) { + return; + } + siglongjmp(dlsym_sigjmp_env, 1); +} + +} // namespace + +void* Dlsym(void* handle, const char* symbol) { + if (!IsKitKat()) { + return dlsym(handle, symbol); + } + + static std::mutex* signal_handler_mutex = new std::mutex(); + std::lock_guard<std::mutex> lock(*signal_handler_mutex); + + ScopedSetTID set_tid(&dlsym_tid); + + ScopedSigactionRestore sig_restore; + if (!sig_restore.ResetAndInstallHandler(SIGFPE, HandleSIGFPE)) { + return nullptr; + } + + if (sigsetjmp(dlsym_sigjmp_env, 1) != 0) { + return nullptr; + } + + return dlsym(handle, symbol); +} + +#else + +void* Dlsym(void* handle, const char* symbol) { + return dlsym(handle, symbol); +} + +#endif // __ANDROID_API__ < 21 + +} // namespace internal +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.h b/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.h new file mode 100644 index 00000000000..ed4083dbc82 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.h @@ -0,0 +1,35 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_ +#define CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_ + +namespace crashpad { +namespace internal { + +//! \brief Provide a wrapper for `dlsym`. +//! +//! dlsym on Android KitKat (4.4.*) raises SIGFPE when searching for a +//! non-existent symbol. This wrapper avoids crashing in this circumstance. +//! https://code.google.com/p/android/issues/detail?id=61799 +//! +//! The parameters and return value for this function are the same as for +//! `dlsym`, but a return value for `dlerror` may not be set in the event of an +//! error. +void* Dlsym(void* handle, const char* symbol); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_ diff --git a/chromium/third_party/crashpad/crashpad/compat/android/sys/epoll.cc b/chromium/third_party/crashpad/crashpad/compat/android/sys/epoll.cc index 64de763fad6..7fd3bb3781b 100644 --- a/chromium/third_party/crashpad/crashpad/compat/android/sys/epoll.cc +++ b/chromium/third_party/crashpad/crashpad/compat/android/sys/epoll.cc @@ -18,18 +18,17 @@ #include <sys/syscall.h> #include <unistd.h> +#include "dlfcn_internal.h" + #if __ANDROID_API__ < 21 extern "C" { int epoll_create1(int flags) { - static const auto epoll_create1_p = - reinterpret_cast<int (*)(int)>(dlsym(RTLD_DEFAULT, "epoll_create1")); - if (epoll_create1_p) { - return epoll_create1_p(flags); - } - - return syscall(SYS_epoll_create1, flags); + static const auto epoll_create1_p = reinterpret_cast<int (*)(int)>( + crashpad::internal::Dlsym(RTLD_DEFAULT, "epoll_create1")); + return epoll_create1_p ? epoll_create1_p(flags) + : syscall(SYS_epoll_create1, flags); } } // extern "C" diff --git a/chromium/third_party/crashpad/crashpad/compat/android/sys/mman.cc b/chromium/third_party/crashpad/crashpad/compat/android/sys/mman.cc index f4d722c9d22..4c29a9dff98 100644 --- a/chromium/third_party/crashpad/crashpad/compat/android/sys/mman.cc +++ b/chromium/third_party/crashpad/crashpad/compat/android/sys/mman.cc @@ -19,6 +19,8 @@ #include <stdint.h> #include <unistd.h> +#include "dlfcn_internal.h" + #if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 // Bionic has provided a wrapper for __mmap2() since the beginning of time. See @@ -87,8 +89,8 @@ void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) { // Use the system’s mmap64() wrapper if available. It will be available on // Android 5.0 (“Lollipop”) and later. using Mmap64Type = void* (*)(void*, size_t, int, int, int, off64_t); - static const Mmap64Type mmap64 = - reinterpret_cast<Mmap64Type>(dlsym(RTLD_DEFAULT, "mmap64")); + static const Mmap64Type mmap64 = reinterpret_cast<Mmap64Type>( + crashpad::internal::Dlsym(RTLD_DEFAULT, "mmap64")); if (mmap64) { return mmap64(addr, size, prot, flags, fd, offset); } diff --git a/chromium/third_party/crashpad/crashpad/compat/compat.gyp b/chromium/third_party/crashpad/crashpad/compat/compat.gyp index d3b785b98c2..c5fe350f613 100644 --- a/chromium/third_party/crashpad/crashpad/compat/compat.gyp +++ b/chromium/third_party/crashpad/crashpad/compat/compat.gyp @@ -21,6 +21,8 @@ 'target_name': 'crashpad_compat', 'type': 'static_library', 'sources': [ + 'android/dlfcn_internal.cc', + 'android/dlfcn_internal.h', 'android/elf.h', 'android/linux/elf.h', 'android/linux/prctl.h', diff --git a/chromium/third_party/crashpad/crashpad/compat/linux/signal.h b/chromium/third_party/crashpad/crashpad/compat/linux/signal.h index 62b9c08636d..4e1cdc1f4b6 100644 --- a/chromium/third_party/crashpad/crashpad/compat/linux/signal.h +++ b/chromium/third_party/crashpad/crashpad/compat/linux/signal.h @@ -18,10 +18,43 @@ #include_next <signal.h> // Missing from glibc and bionic-x86_64 + #if defined(__x86_64__) || defined(__i386__) #if !defined(X86_FXSR_MAGIC) #define X86_FXSR_MAGIC 0x0000 #endif #endif // __x86_64__ || __i386__ +#if defined(__aarch64__) || defined(__arm__) + +#if !defined(FPSIMD_MAGIC) +#define FPSIMD_MAGIC 0x46508001 +#endif + +#if !defined(ESR_MAGIC) +#define ESR_MAGIC 0x45535201 +#endif + +#if !defined(EXTRA_MAGIC) +#define EXTRA_MAGIC 0x45585401 +#endif + +#if !defined(VFP_MAGIC) +#define VFP_MAGIC 0x56465001 +#endif + +#if !defined(CRUNCH_MAGIC) +#define CRUNCH_MAGIC 0x5065cf03 +#endif + +#if !defined(DUMMY_MAGIC) +#define DUMMY_MAGIC 0xb0d9ed01 +#endif + +#if !defined(IWMMXT_MAGIC) +#define IWMMXT_MAGIC 0x12ef842a +#endif + +#endif // __aarch64__ || __arm__ + #endif // CRASHPAD_COMPAT_LINUX_SIGNAL_H_ diff --git a/chromium/third_party/crashpad/crashpad/handler/BUILD.gn b/chromium/third_party/crashpad/crashpad/handler/BUILD.gn index 71037bb04d5..9c337697b60 100644 --- a/chromium/third_party/crashpad/crashpad/handler/BUILD.gn +++ b/chromium/third_party/crashpad/crashpad/handler/BUILD.gn @@ -37,7 +37,7 @@ static_library("handler") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/exception_handler_server.cc", "linux/exception_handler_server.h", @@ -89,7 +89,7 @@ source_set("handler_test") { "minidump_to_upload_parameters_test.cc", ] - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/exception_handler_server_test.cc" ] } diff --git a/chromium/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc b/chromium/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc index 3369c4f8861..6bb55cdaa31 100644 --- a/chromium/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc +++ b/chromium/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc @@ -24,6 +24,7 @@ #include <utility> +#include "base/compiler_specific.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/string_number_conversions.h" @@ -150,7 +151,7 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider { if (HaveCapSysPtrace()) { return Strategy::kDirectPtrace; } - // fallthrough + FALLTHROUGH; case PtraceScope::kNoAttach: LOG(WARNING) << "no ptrace"; return Strategy::kNoPtrace; diff --git a/chromium/third_party/crashpad/crashpad/handler/win/crash_other_program.cc b/chromium/third_party/crashpad/crashpad/handler/win/crash_other_program.cc index 2b62aea269a..55c06994ee3 100644 --- a/chromium/third_party/crashpad/crashpad/handler/win/crash_other_program.cc +++ b/chromium/third_party/crashpad/crashpad/handler/win/crash_other_program.cc @@ -33,7 +33,7 @@ namespace { constexpr DWORD kCrashAndDumpTargetExitCode = 0xdeadbea7; -bool CrashAndDumpTarget(const CrashpadClient& client, HANDLE process) { +bool CrashAndDumpTarget(HANDLE process) { DWORD target_pid = GetProcessId(process); ScopedFileHANDLE thread_snap(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)); @@ -61,7 +61,7 @@ bool CrashAndDumpTarget(const CrashpadClient& client, HANDLE process) { PLOG(ERROR) << "OpenThread"; return false; } - if (!client.DumpAndCrashTargetProcess( + if (!CrashpadClient::DumpAndCrashTargetProcess( process, thread.get(), kCrashAndDumpTargetExitCode)) { return false; } @@ -109,11 +109,12 @@ int CrashOtherProgram(int argc, wchar_t* argv[]) { DWORD expect_exit_code; if (argc == 3 && wcscmp(argv[2], L"noexception") == 0) { expect_exit_code = CrashpadClient::kTriggeredExceptionCode; - if (!client.DumpAndCrashTargetProcess(child.process_handle(), 0, 0)) + if (!CrashpadClient::DumpAndCrashTargetProcess( + child.process_handle(), 0, 0)) return EXIT_FAILURE; } else { expect_exit_code = kCrashAndDumpTargetExitCode; - if (!CrashAndDumpTarget(client, child.process_handle())) { + if (!CrashAndDumpTarget(child.process_handle())) { return EXIT_FAILURE; } } diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h index 1226b654aa5..a7328cad0f6 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h @@ -336,6 +336,102 @@ struct alignas(16) MinidumpContextAMD64 { //! \} }; +//! \brief 32-bit ARM-specifc flags for MinidumpContextARM::context_flags. +enum MinidumpContextARMFlags : uint32_t { + //! \brief Identifies the context structure as 32-bit ARM. + kMinidumpContextARM = 0x40000000, + + //! \brief Indicates the validity of integer regsiters. + //! + //! Regsiters `r0`-`r15` and `cpsr` are valid. + kMinidumpContextARMInteger = kMinidumpContextARM | 0x00000002, + + //! \brief Inidicates the validity of VFP regsiters. + //! + //! Registers `d0`-`d31` and `fpscr` are valid. + kMinidumpContextARMVFP = kMinidumpContextARM | 0x00000004, + + //! \brief Indicates the validity of all registers. + kMinidumpContextARMAll = kMinidumpContextARMInteger | kMinidumpContextARMVFP, +}; + +//! \brief A 32-bit ARM CPU context (register state) carried in a minidump file. +struct MinidumpContextARM { + //! \brief A bitfield composed of values of #MinidumpContextFlags and + //! #MinidumpContextARMFlags. + //! + //! This field identifies the context structure as a 32-bit ARM CPU context, + //! and indicates which other fields in the structure are valid. + uint32_t context_flags; + + //! \brief General-purpose registers `r0`-`r15`. + uint32_t regs[11]; + uint32_t fp; // r11 + uint32_t ip; // r12 + uint32_t sp; // r13 + uint32_t lr; // r14 + uint32_t pc; // r15 + + //! \brief Current program status register. + uint32_t cpsr; + + //! \brief Floating-point status and control register. + uint32_t fpscr; + + //! \brief VFP registers `d0`-`d31`. + uint64_t vfp[32]; + + //! \brief This space is unused. It is included for compatibility with + //! breakpad (which also doesn't use it). + uint32_t extra[8]; +}; + +//! \brief 64-bit ARM-specifc flags for MinidumpContextARM64::context_flags. +enum MinidumpContextARM64Flags : uint32_t { + //! \brief Identifies the context structure as 64-bit ARM. + kMinidumpContextARM64 = 0x80000000, + + //! \brief Indicates the validty of integer registers. + //! + //! Registers `x0`-`x31`, `pc`, and `cpsr`. + kMinidumpContextARM64Integer = kMinidumpContextARM64 | 0x00000002, + + //! \brief Indicates the validity of fpsimd registers. + //! + //! Registers `v0`-`v31`, `fpsr`, and `fpcr` are valid. + kMinidumpContextARM64Fpsimd = kMinidumpContextARM64 | 0x00000004, + + //! \brief Indicates the validity of all registers. + kMinidumpContextARM64All = + kMinidumpContextARM64Integer | kMinidumpContextARM64Fpsimd, +}; + +//! \brief A 64-bit ARM CPU context (register state) carried in a minidump file. +struct MinidumpContextARM64 { + uint64_t context_flags; + + //! \brief General-purpose registers `x0`-`x30`. + uint64_t regs[31]; + + //! \brief Stack pointer or `x31`. + uint64_t sp; + + //! \brief Program counter. + uint64_t pc; + + //! \brief Current program status register. + uint32_t cpsr; + + //! \brief Floating-point status register. + uint32_t fpsr; + + //! \brief Floating-point control register. + uint32_t fpcr; + + //! \brief NEON registers `v0`-`v31`. + uint128_struct fpsimd[32]; +}; + } // namespace crashpad #endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_ diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc index 218d7775a72..2fa2e53e38f 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc @@ -73,6 +73,20 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) { break; } + case kCPUArchitectureARM: { + context = std::make_unique<MinidumpContextARMWriter>(); + reinterpret_cast<MinidumpContextARMWriter*>(context.get()) + ->InitializeFromSnapshot(context_snapshot->arm); + break; + } + + case kCPUArchitectureARM64: { + context = std::make_unique<MinidumpContextARM64Writer>(); + reinterpret_cast<MinidumpContextARM64Writer*>(context.get()) + ->InitializeFromSnapshot(context_snapshot->arm64); + break; + } + default: { LOG(ERROR) << "unknown context architecture " << context_snapshot->architecture; @@ -239,4 +253,90 @@ size_t MinidumpContextAMD64Writer::ContextSize() const { return sizeof(context_); } +MinidumpContextARMWriter::MinidumpContextARMWriter() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextARM; +} + +MinidumpContextARMWriter::~MinidumpContextARMWriter() = default; + +void MinidumpContextARMWriter::InitializeFromSnapshot( + const CPUContextARM* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextARM); + + context_.context_flags = kMinidumpContextARMAll; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRS size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.fp = context_snapshot->fp; + context_.ip = context_snapshot->ip; + context_.sp = context_snapshot->sp; + context_.lr = context_snapshot->lr; + context_.pc = context_snapshot->pc; + context_.cpsr = context_snapshot->cpsr; + + context_.fpscr = context_snapshot->vfp_regs.fpscr; + static_assert(sizeof(context_.vfp) == sizeof(context_snapshot->vfp_regs.vfp), + "VFP size mismatch"); + memcpy(context_.vfp, context_snapshot->vfp_regs.vfp, sizeof(context_.vfp)); + + memset(context_.extra, 0, sizeof(context_.extra)); +} + +bool MinidumpContextARMWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextARMWriter::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + +MinidumpContextARM64Writer::MinidumpContextARM64Writer() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextARM64; +} + +MinidumpContextARM64Writer::~MinidumpContextARM64Writer() = default; + +void MinidumpContextARM64Writer::InitializeFromSnapshot( + const CPUContextARM64* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextARM64); + + context_.context_flags = kMinidumpContextARM64All; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRs size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.sp = context_snapshot->sp; + context_.pc = context_snapshot->pc; + + if (context_snapshot->pstate > + std::numeric_limits<decltype(context_.cpsr)>::max()) { + LOG(WARNING) << "pstate truncation"; + } + context_.cpsr = + static_cast<decltype(context_.cpsr)>(context_snapshot->pstate); + + context_.fpsr = context_snapshot->fpsr; + context_.fpcr = context_snapshot->fpcr; + static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd), + "FPSIMD size mismatch"); + memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd)); +} + +bool MinidumpContextARM64Writer::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextARM64Writer::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h index 25d717e585d..fb3b1513fb3 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h @@ -155,6 +155,86 @@ class MinidumpContextAMD64Writer final : public MinidumpContextWriter { DISALLOW_COPY_AND_ASSIGN(MinidumpContextAMD64Writer); }; +//! \brief The writer for a MinidumpContextARM structure in a minidump file. +class MinidumpContextARMWriter final : public MinidumpContextWriter { + public: + MinidumpContextARMWriter(); + ~MinidumpContextARMWriter() override; + + //! \brief Initializes the MinidumpContextARM based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextARM* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextARM* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextARM context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextARMWriter); +}; + +//! \brief The writer for a MinidumpContextARM64 structure in a minidump file. +class MinidumpContextARM64Writer final : public MinidumpContextWriter { + public: + MinidumpContextARM64Writer(); + ~MinidumpContextARM64Writer() override; + + //! \brief Initializes the MinidumpContextARM64 based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextARM64* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextARM64* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextARM64 context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContextARM64Writer); +}; + } // namespace crashpad #endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_ diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc index 82b4db751ff..0122683cb1d 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc @@ -28,6 +28,20 @@ namespace crashpad { namespace test { namespace { +template <typename Writer, typename Context> +void EmptyContextTest(void (*expect_context)(uint32_t, const Context*, bool)) { + Writer context_writer; + StringFile string_file; + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), sizeof(Context)); + + const Context* observed = + MinidumpWritableAtRVA<Context>(string_file.string(), 0); + ASSERT_TRUE(observed); + + expect_context(0, observed, false); +} + TEST(MinidumpContextWriter, MinidumpContextX86Writer) { StringFile string_file; @@ -36,16 +50,8 @@ TEST(MinidumpContextWriter, MinidumpContextX86Writer) { // context. SCOPED_TRACE("zero"); - MinidumpContextX86Writer context_writer; - - EXPECT_TRUE(context_writer.WriteEverything(&string_file)); - ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextX86)); - - const MinidumpContextX86* observed = - MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0); - ASSERT_TRUE(observed); - - ExpectMinidumpContextX86(0, observed, false); + EmptyContextTest<MinidumpContextX86Writer, MinidumpContextX86>( + ExpectMinidumpContextX86); } { @@ -85,16 +91,8 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { // context. SCOPED_TRACE("zero"); - MinidumpContextAMD64Writer context_writer; - - EXPECT_TRUE(context_writer.WriteEverything(&string_file)); - ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextAMD64)); - - const MinidumpContextAMD64* observed = - MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0); - ASSERT_TRUE(observed); - - ExpectMinidumpContextAMD64(0, observed, false); + EmptyContextTest<MinidumpContextAMD64Writer, MinidumpContextAMD64>( + ExpectMinidumpContextAMD64); } { @@ -117,48 +115,72 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { } } -TEST(MinidumpContextWriter, CreateFromSnapshot_X86) { - constexpr uint32_t kSeed = 32; - - CPUContextX86 context_snapshot_x86; - CPUContext context_snapshot; - context_snapshot.x86 = &context_snapshot_x86; - InitializeCPUContextX86(&context_snapshot, kSeed); - +template <typename Writer, typename Context> +void FromSnapshotTest(const CPUContext& snapshot_context, + void (*expect_context)(uint32_t, const Context*, bool), + uint32_t seed) { std::unique_ptr<MinidumpContextWriter> context_writer = - MinidumpContextWriter::CreateFromSnapshot(&context_snapshot); + MinidumpContextWriter::CreateFromSnapshot(&snapshot_context); ASSERT_TRUE(context_writer); StringFile string_file; ASSERT_TRUE(context_writer->WriteEverything(&string_file)); - const MinidumpContextX86* observed = - MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0); + const Context* observed = + MinidumpWritableAtRVA<Context>(string_file.string(), 0); ASSERT_TRUE(observed); - ExpectMinidumpContextX86(kSeed, observed, true); + expect_context(seed, observed, true); } -TEST(MinidumpContextWriter, CreateFromSnapshot_AMD64) { - constexpr uint32_t kSeed = 64; +TEST(MinidumpContextWriter, X86_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextX86 context_x86; + CPUContext context; + context.x86 = &context_x86; + InitializeCPUContextX86(&context, kSeed); + FromSnapshotTest<MinidumpContextX86Writer, MinidumpContextX86>( + context, ExpectMinidumpContextX86, kSeed); +} - CPUContextX86_64 context_snapshot_x86_64; - CPUContext context_snapshot; - context_snapshot.x86_64 = &context_snapshot_x86_64; - InitializeCPUContextX86_64(&context_snapshot, kSeed); +TEST(MinidumpContextWriter, AMD64_FromSnapshot) { + constexpr uint32_t kSeed = 64; + CPUContextX86_64 context_x86_64; + CPUContext context; + context.x86_64 = &context_x86_64; + InitializeCPUContextX86_64(&context, kSeed); + FromSnapshotTest<MinidumpContextAMD64Writer, MinidumpContextAMD64>( + context, ExpectMinidumpContextAMD64, kSeed); +} - std::unique_ptr<MinidumpContextWriter> context_writer = - MinidumpContextWriter::CreateFromSnapshot(&context_snapshot); - ASSERT_TRUE(context_writer); +TEST(MinidumpContextWriter, ARM_Zeros) { + EmptyContextTest<MinidumpContextARMWriter, MinidumpContextARM>( + ExpectMinidumpContextARM); +} - StringFile string_file; - ASSERT_TRUE(context_writer->WriteEverything(&string_file)); +TEST(MinidumpContextWRiter, ARM64_Zeros) { + EmptyContextTest<MinidumpContextARM64Writer, MinidumpContextARM64>( + ExpectMinidumpContextARM64); +} - const MinidumpContextAMD64* observed = - MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0); - ASSERT_TRUE(observed); +TEST(MinidumpContextWriter, ARM_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextARM context_arm; + CPUContext context; + context.arm = &context_arm; + InitializeCPUContextARM(&context, kSeed); + FromSnapshotTest<MinidumpContextARMWriter, MinidumpContextARM>( + context, ExpectMinidumpContextARM, kSeed); +} - ExpectMinidumpContextAMD64(kSeed, observed, true); +TEST(MinidumpContextWriter, ARM64_FromSnapshot) { + constexpr uint32_t kSeed = 64; + CPUContextARM64 context_arm64; + CPUContext context; + context.arm64 = &context_arm64; + InitializeCPUContextARM64(&context, kSeed); + FromSnapshotTest<MinidumpContextARM64Writer, MinidumpContextARM64>( + context, ExpectMinidumpContextARM64, kSeed); } } // namespace diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc index 7371039230b..634d3f1af19 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc @@ -84,14 +84,16 @@ TEST(MinidumpMemoryInfoWriter, OneRegion) { auto memory_map_region = std::make_unique<TestMemoryMapRegionSnapshot>(); - MINIDUMP_MEMORY_INFO mmi = {0}; + MINIDUMP_MEMORY_INFO mmi; mmi.BaseAddress = 0x12340000; mmi.AllocationBase = 0x12000000; mmi.AllocationProtect = PAGE_READWRITE; + mmi.__alignment1 = 0; mmi.RegionSize = 0x6000; mmi.State = MEM_COMMIT; mmi.Protect = PAGE_NOACCESS; mmi.Type = MEM_PRIVATE; + mmi.__alignment2 = 0; memory_map_region->SetMindumpMemoryInfo(mmi); std::vector<const MemoryMapRegionSnapshot*> memory_map; diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc index f6aca081bbd..0e5f0861247 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc @@ -14,6 +14,8 @@ #include "minidump/minidump_memory_writer.h" +#include <algorithm> +#include <iterator> #include <utility> #include "base/auto_reset.h" @@ -37,7 +39,7 @@ SnapshotMinidumpMemoryWriter::~SnapshotMinidumpMemoryWriter() {} bool SnapshotMinidumpMemoryWriter::MemorySnapshotDelegateRead(void* data, size_t size) { DCHECK_EQ(state(), kStateWritable); - DCHECK_EQ(size, UnderlyingSnapshot().Size()); + DCHECK_EQ(size, UnderlyingSnapshot()->Size()); return file_writer_->Write(data, size); } @@ -89,7 +91,7 @@ size_t SnapshotMinidumpMemoryWriter::Alignment() { size_t SnapshotMinidumpMemoryWriter::SizeOfObject() { DCHECK_GE(state(), kStateFrozen); - return UnderlyingSnapshot().Size(); + return UnderlyingSnapshot()->Size(); } bool SnapshotMinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) { @@ -99,7 +101,7 @@ bool SnapshotMinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) { // object’s own memory_descriptor_ field. DCHECK_GE(registered_memory_descriptors_.size(), 1u); - uint64_t base_address = UnderlyingSnapshot().Address(); + uint64_t base_address = UnderlyingSnapshot()->Address(); decltype(registered_memory_descriptors_[0]->StartOfMemoryRange) local_address; if (!AssignIfInRange(&local_address, base_address)) { LOG(ERROR) << "base_address " << base_address << " out of range"; @@ -125,10 +127,11 @@ internal::MinidumpWritable::Phase SnapshotMinidumpMemoryWriter::WritePhase() { MinidumpMemoryListWriter::MinidumpMemoryListWriter() : MinidumpStreamWriter(), - memory_writers_(), + non_owned_memory_writers_(), children_(), - memory_list_base_() { -} + snapshots_created_during_merge_(), + all_memory_writers_(), + memory_list_base_() {} MinidumpMemoryListWriter::~MinidumpMemoryListWriter() { } @@ -148,25 +151,80 @@ void MinidumpMemoryListWriter::AddMemory( std::unique_ptr<SnapshotMinidumpMemoryWriter> memory_writer) { DCHECK_EQ(state(), kStateMutable); - AddExtraMemory(memory_writer.get()); children_.push_back(std::move(memory_writer)); } -void MinidumpMemoryListWriter::AddExtraMemory( +void MinidumpMemoryListWriter::AddNonOwnedMemory( SnapshotMinidumpMemoryWriter* memory_writer) { DCHECK_EQ(state(), kStateMutable); - memory_writers_.push_back(memory_writer); + non_owned_memory_writers_.push_back(memory_writer); +} + +void MinidumpMemoryListWriter::CoalesceOwnedMemory() { + if (children_.empty()) + return; + + DropRangesThatOverlapNonOwned(); + + std::sort(children_.begin(), + children_.end(), + [](const std::unique_ptr<SnapshotMinidumpMemoryWriter>& a_ptr, + const std::unique_ptr<SnapshotMinidumpMemoryWriter>& b_ptr) { + const MemorySnapshot* a = a_ptr->UnderlyingSnapshot(); + const MemorySnapshot* b = b_ptr->UnderlyingSnapshot(); + if (a->Address() == b->Address()) { + return a->Size() < b->Size(); + } + return a->Address() < b->Address(); + }); + + // Remove any empty ranges. + children_.erase( + std::remove_if(children_.begin(), + children_.end(), + [](const auto& snapshot) { + return snapshot->UnderlyingSnapshot()->Size() == 0; + }), + children_.end()); + + std::vector<std::unique_ptr<SnapshotMinidumpMemoryWriter>> all_merged; + all_merged.push_back(std::move(children_.front())); + for (size_t i = 1; i < children_.size(); ++i) { + SnapshotMinidumpMemoryWriter* top = all_merged.back().get(); + auto& child = children_[i]; + if (!DetermineMergedRange( + child->UnderlyingSnapshot(), top->UnderlyingSnapshot(), nullptr)) { + // If it doesn't overlap with the current range, push it. + all_merged.push_back(std::move(child)); + } else { + // Otherwise, merge and update the current element. + std::unique_ptr<const MemorySnapshot> merged( + top->UnderlyingSnapshot()->MergeWithOtherSnapshot( + child->UnderlyingSnapshot())); + top->SetSnapshot(merged.get()); + snapshots_created_during_merge_.push_back(std::move(merged)); + } + } + std::swap(children_, all_merged); } bool MinidumpMemoryListWriter::Freeze() { DCHECK_EQ(state(), kStateMutable); + CoalesceOwnedMemory(); + + std::copy(non_owned_memory_writers_.begin(), + non_owned_memory_writers_.end(), + std::back_inserter(all_memory_writers_)); + for (const auto& ptr : children_) + all_memory_writers_.push_back(ptr.get()); + if (!MinidumpStreamWriter::Freeze()) { return false; } - size_t memory_region_count = memory_writers_.size(); + size_t memory_region_count = all_memory_writers_.size(); CHECK_LE(children_.size(), memory_region_count); if (!AssignIfInRange(&memory_list_base_.NumberOfMemoryRanges, @@ -181,15 +239,15 @@ bool MinidumpMemoryListWriter::Freeze() { size_t MinidumpMemoryListWriter::SizeOfObject() { DCHECK_GE(state(), kStateFrozen); - DCHECK_LE(children_.size(), memory_writers_.size()); + DCHECK_LE(children_.size(), all_memory_writers_.size()); return sizeof(memory_list_base_) + - memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + all_memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); } std::vector<internal::MinidumpWritable*> MinidumpMemoryListWriter::Children() { DCHECK_GE(state(), kStateFrozen); - DCHECK_LE(children_.size(), memory_writers_.size()); + DCHECK_LE(children_.size(), all_memory_writers_.size()); std::vector<MinidumpWritable*> children; for (const auto& child : children_) { @@ -207,7 +265,8 @@ bool MinidumpMemoryListWriter::WriteObject(FileWriterInterface* file_writer) { iov.iov_len = sizeof(memory_list_base_); std::vector<WritableIoVec> iovecs(1, iov); - for (const SnapshotMinidumpMemoryWriter* memory_writer : memory_writers_) { + for (const SnapshotMinidumpMemoryWriter* memory_writer : + all_memory_writers_) { iov.iov_base = memory_writer->MinidumpMemoryDescriptor(); iov.iov_len = sizeof(MINIDUMP_MEMORY_DESCRIPTOR); iovecs.push_back(iov); @@ -220,4 +279,23 @@ MinidumpStreamType MinidumpMemoryListWriter::StreamType() const { return kMinidumpStreamTypeMemoryList; } +void MinidumpMemoryListWriter::DropRangesThatOverlapNonOwned() { + std::vector<std::unique_ptr<SnapshotMinidumpMemoryWriter>> non_overlapping; + non_overlapping.reserve(children_.size()); + for (auto& child_ptr : children_) { + bool overlaps = false; + for (const auto* non_owned : non_owned_memory_writers_) { + if (DetermineMergedRange(child_ptr->UnderlyingSnapshot(), + non_owned->UnderlyingSnapshot(), + nullptr)) { + overlaps = true; + break; + } + } + if (!overlaps) + non_overlapping.push_back(std::move(child_ptr)); + } + std::swap(children_, non_overlapping); +} + } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h index 955209076ce..4f4f27f6cde 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h @@ -60,7 +60,15 @@ class SnapshotMinidumpMemoryWriter : public internal::MinidumpWritable, //! \note Valid in #kStateFrozen or any preceding state. void RegisterMemoryDescriptor(MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor); + //! \brief Sets the underlying memory snapshot. Does not take ownership of \a + //! memory_snapshot. + void SetSnapshot(const MemorySnapshot* memory_snapshot) { + memory_snapshot_ = memory_snapshot; + } + private: + friend class MinidumpMemoryListWriter; + // MemorySnapshot::Delegate: bool MemorySnapshotDelegateRead(void* data, size_t size) override; @@ -95,7 +103,7 @@ class SnapshotMinidumpMemoryWriter : public internal::MinidumpWritable, //! \brief Gets the underlying memory snapshot that the memory writer will //! write to the minidump. - const MemorySnapshot& UnderlyingSnapshot() const { return *memory_snapshot_; } + const MemorySnapshot* UnderlyingSnapshot() const { return memory_snapshot_; } MINIDUMP_MEMORY_DESCRIPTOR memory_descriptor_; @@ -147,7 +155,7 @@ class MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter { //! a SnapshotMinidumpMemoryWriter for thread stack memory, is an example. //! //! \note Valid in #kStateMutable. - void AddExtraMemory(SnapshotMinidumpMemoryWriter* memory_writer); + void AddNonOwnedMemory(SnapshotMinidumpMemoryWriter* memory_writer); protected: // MinidumpWritable: @@ -159,9 +167,35 @@ class MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter { // MinidumpStreamWriter: MinidumpStreamType StreamType() const override; + //! \brief Merges any overlapping and abutting memory ranges that were added + //! via AddFromSnapshot() and AddMemory() into single entries. + //! + //! This is expected to be called once just before writing, generally from + //! Freeze(). + //! + //! This function has the side-effect of merging owned ranges, dropping any + //! owned ranges that overlap with non-owned ranges, removing empty ranges, + //! and sorting all ranges by address. + //! + //! Per its name, this coalesces owned memory, however, this is not a complete + //! solution for ensuring that no overlapping memory ranges are emitted in the + //! minidump. In particular, if AddNonOwnedMemory() is used to add multiple + //! overlapping ranges, then overlapping ranges will still be emitted to the + //! minidump. Currently, AddNonOwnedMemory() is used only for adding thread + //! stacks, so overlapping shouldn't be a problem in practice. For more + //! details see https://crashpad.chromium.org/bug/61 and + //! https://crrev.com/c/374539. + void CoalesceOwnedMemory(); + private: - std::vector<SnapshotMinidumpMemoryWriter*> memory_writers_; // weak + //! \brief Drops children_ ranges that overlap non_owned_memory_writers_. + void DropRangesThatOverlapNonOwned(); + + std::vector<SnapshotMinidumpMemoryWriter*> non_owned_memory_writers_; // weak std::vector<std::unique_ptr<SnapshotMinidumpMemoryWriter>> children_; + std::vector<std::unique_ptr<const MemorySnapshot>> + snapshots_created_during_merge_; + std::vector<SnapshotMinidumpMemoryWriter*> all_memory_writers_; // weak MINIDUMP_MEMORY_LIST memory_list_base_; DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryListWriter); diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc index 0290b20dd4c..acff9c299fb 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc @@ -241,7 +241,7 @@ TEST(MinidumpMemoryWriter, ExtraMemory) { std::make_unique<TestMemoryStream>(kBaseAddress0, kSize0, kValue0); auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>(); - memory_list_writer->AddExtraMemory(test_memory_stream->memory()); + memory_list_writer->AddNonOwnedMemory(test_memory_stream->memory()); ASSERT_TRUE(minidump_file_writer.AddStream(std::move(test_memory_stream))); @@ -305,7 +305,7 @@ TEST(MinidumpMemoryWriter, AddFromSnapshot) { expect_memory_descriptors[0].Memory.DataSize = 0x1000; values[0] = 0x01; - expect_memory_descriptors[1].StartOfMemoryRange = 0x1000; + expect_memory_descriptors[1].StartOfMemoryRange = 0x2000; expect_memory_descriptors[1].Memory.DataSize = 0x2000; values[1] = 0xf4; @@ -353,6 +353,298 @@ TEST(MinidumpMemoryWriter, AddFromSnapshot) { } } +TEST(MinidumpMemoryWriter, CoalesceExplicitMultiple) { + MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[4] = {}; + uint8_t values[arraysize(expect_memory_descriptors)] = {}; + + expect_memory_descriptors[0].StartOfMemoryRange = 0; + expect_memory_descriptors[0].Memory.DataSize = 1000; + values[0] = 0x01; + + expect_memory_descriptors[1].StartOfMemoryRange = 10000; + expect_memory_descriptors[1].Memory.DataSize = 2000; + values[1] = 0xf4; + + expect_memory_descriptors[2].StartOfMemoryRange = 0x1111111111111111; + expect_memory_descriptors[2].Memory.DataSize = 1024; + values[2] = 0x99; + + expect_memory_descriptors[3].StartOfMemoryRange = 0xfedcba9876543210; + expect_memory_descriptors[3].Memory.DataSize = 1024; + values[3] = 0x88; + + struct { + uint64_t base; + size_t size; + uint8_t value; + } snapshots_to_add[] = { + // Various overlapping. + {0, 500, 0x01}, + {0, 500, 0x01}, + {250, 500, 0x01}, + {600, 400, 0x01}, + + // Empty removed. + {0, 0, 0xbb}, + {300, 0, 0xcc}, + {1000, 0, 0xdd}, + {12000, 0, 0xee}, + + // Abutting. + {10000, 500, 0xf4}, + {10500, 500, 0xf4}, + {11000, 1000, 0xf4}, + + // Large base addresses. + { 0xfedcba9876543210, 1024, 0x88 }, + { 0x1111111111111111, 1024, 0x99 }, + }; + + std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner; + std::vector<const MemorySnapshot*> memory_snapshots; + for (const auto& to_add : snapshots_to_add) { + memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>()); + TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get(); + memory_snapshot->SetAddress(to_add.base); + memory_snapshot->SetSize(to_add.size); + memory_snapshot->SetValue(to_add.value); + memory_snapshots.push_back(memory_snapshot); + } + + auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>(); + memory_list_writer->AddFromSnapshot(memory_snapshots); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(std::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + ASSERT_EQ(4u, memory_list->NumberOfMemoryRanges); + + for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ExpectMinidumpMemoryDescriptorAndContents( + &expect_memory_descriptors[index], + &memory_list->MemoryRanges[index], + string_file.string(), + values[index], + index == memory_list->NumberOfMemoryRanges - 1); + } +} + +struct TestRange { + TestRange(uint64_t base, size_t size) : base(base), size(size) {} + + uint64_t base; + size_t size; +}; + +// Parses a string spec to build a list of ranges suitable for CoalesceTest(). +std::vector<TestRange> ParseCoalesceSpec(const char* spec) { + std::vector<TestRange> result; + enum { kNone, kSpace, kDot } state = kNone; + const char* range_started_at = nullptr; + for (const char* p = spec;; ++p) { + EXPECT_TRUE(*p == ' ' || *p == '.' || *p == 0); + if (*p == ' ' || *p == 0) { + if (state == kDot) { + result.push_back( + TestRange(range_started_at - spec, p - range_started_at)); + } + state = kSpace; + range_started_at = nullptr; + } else if (*p == '.') { + if (state != kDot) { + range_started_at = p; + state = kDot; + } + } + + if (*p == 0) + break; + } + + return result; +} + +TEST(MinidumpMemoryWriter, CoalesceSpecHelperParse) { + const auto empty = ParseCoalesceSpec(""); + ASSERT_EQ(empty.size(), 0u); + + const auto a = ParseCoalesceSpec("..."); + ASSERT_EQ(a.size(), 1u); + EXPECT_EQ(a[0].base, 0u); + EXPECT_EQ(a[0].size, 3u); + + const auto b = ParseCoalesceSpec(" ..."); + ASSERT_EQ(b.size(), 1u); + EXPECT_EQ(b[0].base, 2u); + EXPECT_EQ(b[0].size, 3u); + + const auto c = ParseCoalesceSpec(" ... "); + ASSERT_EQ(c.size(), 1u); + EXPECT_EQ(c[0].base, 2u); + EXPECT_EQ(c[0].size, 3u); + + const auto d = ParseCoalesceSpec(" ... ...."); + ASSERT_EQ(d.size(), 2u); + EXPECT_EQ(d[0].base, 2u); + EXPECT_EQ(d[0].size, 3u); + EXPECT_EQ(d[1].base, 7u); + EXPECT_EQ(d[1].size, 4u); + + const auto e = ParseCoalesceSpec(" ... ...... ... "); + ASSERT_EQ(e.size(), 3u); + EXPECT_EQ(e[0].base, 2u); + EXPECT_EQ(e[0].size, 3u); + EXPECT_EQ(e[1].base, 7u); + EXPECT_EQ(e[1].size, 6u); + EXPECT_EQ(e[2].base, 14u); + EXPECT_EQ(e[2].size, 3u); +} + +constexpr uint8_t kMemoryValue = 0xcd; + +// Builds a coalesce test out of specs of ' ' and '.'. Tests that when the two +// ranges are added and coalesced, the result is equal to expected. +void CoalesceTest(const char* r1_spec, + const char* r2_spec, + const char* expected_spec) { + auto r1 = ParseCoalesceSpec(r1_spec); + auto r2 = ParseCoalesceSpec(r2_spec); + auto expected = ParseCoalesceSpec(expected_spec); + + std::vector<MINIDUMP_MEMORY_DESCRIPTOR> expect_memory_descriptors; + for (const auto& range : expected) { + MINIDUMP_MEMORY_DESCRIPTOR mmd = {}; + mmd.StartOfMemoryRange = range.base; + mmd.Memory.DataSize = static_cast<uint32_t>(range.size); + expect_memory_descriptors.push_back(mmd); + } + + std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner; + std::vector<const MemorySnapshot*> memory_snapshots; + + const auto add_test_memory_snapshots = [&memory_snapshots_owner, + &memory_snapshots]( + std::vector<TestRange> ranges) { + for (const auto& r : ranges) { + memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>()); + TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get(); + memory_snapshot->SetAddress(r.base); + memory_snapshot->SetSize(r.size); + memory_snapshot->SetValue(kMemoryValue); + memory_snapshots.push_back(memory_snapshot); + } + }; + add_test_memory_snapshots(r1); + add_test_memory_snapshots(r2); + + auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>(); + memory_list_writer->AddFromSnapshot(memory_snapshots); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(std::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + ASSERT_EQ(expected.size(), memory_list->NumberOfMemoryRanges); + + for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ExpectMinidumpMemoryDescriptorAndContents( + &expect_memory_descriptors[index], + &memory_list->MemoryRanges[index], + string_file.string(), + kMemoryValue, + index == memory_list->NumberOfMemoryRanges - 1); + } +} + +TEST(MinidumpMemoryWriter, CoalescePairsVariousCases) { + // clang-format off + + CoalesceTest(" .........", + " .......", + /* result */ " .............."); + + CoalesceTest(" .......", + " .........", + /* result */ " .............."); + + CoalesceTest(" ...", + " .........", + /* result */ " ........."); + + CoalesceTest(" .........", + " ......", + /* result */ " ........."); + + CoalesceTest(" ...", + " ........", + /* result */ " ........"); + + CoalesceTest(" ........", + " ...", + /* result */ " ........"); + + CoalesceTest(" ...", + " ........", + /* result */ " ........"); + + CoalesceTest(" ........", + " ...", + /* result */ " ........"); + + CoalesceTest(" ... ", + " ...", + /* result */ " ... ..."); + + CoalesceTest(" ...", + " ... ", + /* result */ " ... ..."); + + CoalesceTest("...", + ".....", + /* result */ "....."); + + CoalesceTest("...", + " ..", + /* result */ "....."); + + CoalesceTest(" .....", + " ..", + /* result */ " ......."); + + CoalesceTest(" ......... ......", + " .......", + /* result */ " .................."); + + CoalesceTest(" .......", + " ......... ......", + /* result */ " .................."); + + CoalesceTest(" .....", + " ......... ......", + /* result */ " ......... ......"); + + CoalesceTest(" ......... ....... .... .", + " ......... ...... ....", + /* result */ " .......................... ......."); + + // clang-format on +} + } // namespace } // namespace test } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc index b8eb7030d9c..382baaf7f6e 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc @@ -84,12 +84,11 @@ TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) { const size_t expected_utf16_units_with_nul = kTestData[index].output_length + 1; - MINIDUMP_STRING tmp = {0}; + MINIDUMP_STRING* tmp; ALLOW_UNUSED_LOCAL(tmp); const size_t expected_utf16_bytes = - expected_utf16_units_with_nul * sizeof(tmp.Buffer[0]); - ASSERT_EQ(string_file.string().size(), - sizeof(MINIDUMP_STRING) + expected_utf16_bytes); + expected_utf16_units_with_nul * sizeof(tmp->Buffer[0]); + ASSERT_EQ(string_file.string().size(), sizeof(*tmp) + expected_utf16_bytes); const MINIDUMP_STRING* minidump_string = MinidumpStringAtRVA(string_file.string(), 0); @@ -129,11 +128,11 @@ TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) { const MINIDUMP_STRING* minidump_string = MinidumpStringAtRVA(string_file.string(), 0); EXPECT_TRUE(minidump_string); - MINIDUMP_STRING tmp = {0}; + MINIDUMP_STRING* tmp; ALLOW_UNUSED_LOCAL(tmp); - EXPECT_EQ(minidump_string->Length, - string_file.string().size() - sizeof(MINIDUMP_STRING) - - sizeof(tmp.Buffer[0])); + EXPECT_EQ( + minidump_string->Length, + string_file.string().size() - sizeof(*tmp) - sizeof(tmp->Buffer[0])); base::string16 output_string = MinidumpStringAtRVAAsString(string_file.string(), 0); EXPECT_FALSE(output_string.empty()); diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc index c3d02472a21..99c599c14c9 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc @@ -39,11 +39,11 @@ void GetSystemInfoStream(const std::string& file_contents, const MINIDUMP_SYSTEM_INFO** system_info, const MINIDUMP_STRING** csd_version) { // The expected number of bytes for the CSD version’s MINIDUMP_STRING::Buffer. - MINIDUMP_STRING tmp = {0}; + MINIDUMP_STRING* tmp; ALLOW_UNUSED_LOCAL(tmp); - const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp.Buffer[0]); + const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp->Buffer[0]); const size_t kCSDVersionBytesWithNUL = - kCSDVersionBytes + sizeof(tmp.Buffer[0]); + kCSDVersionBytes + sizeof(tmp->Buffer[0]); constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); constexpr size_t kSystemInfoStreamOffset = diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc index 4f390ddb7a4..67658e18aad 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc @@ -173,7 +173,7 @@ void MinidumpThreadListWriter::AddThread( if (memory_list_writer_) { SnapshotMinidumpMemoryWriter* stack = thread->Stack(); if (stack) { - memory_list_writer_->AddExtraMemory(stack); + memory_list_writer_->AddNonOwnedMemory(stack); } } diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc index d9e85aa6594..e95c90447e7 100644 --- a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc +++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc @@ -545,7 +545,7 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) { expect_threads[1].ThreadId = 11; expect_threads[1].SuspendCount = 12; expect_threads[1].Priority = 13; - expect_threads[1].Teb = 0xfedcba9876543210; + expect_threads[1].Teb = 0x1111111111111111; expect_threads[1].ThreadContext.DataSize = sizeof(MinidumpContextType); context_seeds[1] = 0x40000001; tebs[1].StartOfMemoryRange = expect_threads[1].Teb; @@ -554,7 +554,7 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) { expect_threads[2].ThreadId = 21; expect_threads[2].SuspendCount = 22; expect_threads[2].Priority = 23; - expect_threads[2].Teb = 0x1111111111111111; + expect_threads[2].Teb = 0xfedcba9876543210; expect_threads[2].Stack.StartOfMemoryRange = 0x3000; expect_threads[2].Stack.Memory.DataSize = 0x300; expect_threads[2].ThreadContext.DataSize = sizeof(MinidumpContextType); diff --git a/chromium/third_party/crashpad/crashpad/snapshot/BUILD.gn b/chromium/third_party/crashpad/crashpad/snapshot/BUILD.gn index 665620fd79d..e1d24002e21 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/BUILD.gn +++ b/chromium/third_party/crashpad/crashpad/snapshot/BUILD.gn @@ -32,6 +32,7 @@ static_library("snapshot") { "exception_snapshot.h", "handle_snapshot.cc", "handle_snapshot.h", + "memory_snapshot.cc", "memory_snapshot.h", "minidump/minidump_annotation_reader.cc", "minidump/minidump_annotation_reader.h", @@ -98,14 +99,10 @@ static_library("snapshot") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ - "elf/elf_dynamic_array_reader.cc", - "elf/elf_dynamic_array_reader.h", - "elf/elf_image_reader.cc", - "elf/elf_image_reader.h", - "elf/elf_symbol_table_reader.cc", - "elf/elf_symbol_table_reader.h", + "crashpad_types/image_annotation_reader.cc", + "crashpad_types/image_annotation_reader.h", "linux/cpu_context_linux.cc", "linux/cpu_context_linux.h", "linux/debug_rendezvous.cc", @@ -114,6 +111,8 @@ static_library("snapshot") { "linux/exception_snapshot_linux.h", "linux/memory_snapshot_linux.cc", "linux/memory_snapshot_linux.h", + "linux/module_snapshot_linux.cc", + "linux/module_snapshot_linux.h", "linux/process_reader.cc", "linux/process_reader.h", "linux/process_snapshot_linux.cc", @@ -126,6 +125,19 @@ static_library("snapshot") { ] } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ + "crashpad_types/crashpad_info_reader.cc", + "crashpad_types/crashpad_info_reader.h", + "elf/elf_dynamic_array_reader.cc", + "elf/elf_dynamic_array_reader.h", + "elf/elf_image_reader.cc", + "elf/elf_image_reader.h", + "elf/elf_symbol_table_reader.cc", + "elf/elf_symbol_table_reader.h", + ] + } + if (crashpad_is_win) { sources += [ "win/capture_memory_delegate_win.cc", @@ -252,7 +264,7 @@ static_library("test_support") { config("snapshot_test_link") { visibility = [ ":*" ] - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { # There’s no way to make the link depend on this file. “inputs” doesn’t have # the intended effect in a config. https://crbug.com/781858, # https://crbug.com/796187. @@ -268,6 +280,7 @@ source_set("snapshot_test") { sources = [ "cpu_context_test.cc", + "memory_snapshot_test.cc", "minidump/process_snapshot_minidump_test.cc", ] @@ -283,11 +296,9 @@ source_set("snapshot_test") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ - "elf/elf_image_reader_test.cc", - "elf/elf_image_reader_test_note.S", - "elf/test_exported_symbols.sym", + "crashpad_types/image_annotation_reader_test.cc", "linux/debug_rendezvous_test.cc", "linux/exception_snapshot_linux_test.cc", "linux/process_reader_test.cc", @@ -297,6 +308,15 @@ source_set("snapshot_test") { sources += [ "crashpad_info_client_options_test.cc" ] } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ + "crashpad_types/crashpad_info_reader_test.cc", + "elf/elf_image_reader_test.cc", + "elf/elf_image_reader_test_note.S", + "elf/test_exported_symbols.sym", + ] + } + if (crashpad_is_win) { sources += [ "api/module_annotations_win_test.cc", @@ -345,10 +365,14 @@ source_set("snapshot_test") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { libs = [ "dl" ] } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + data_deps += [ ":crashpad_snapshot_test_both_dt_hash_styles" ] + } + if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union @@ -396,6 +420,18 @@ loadable_module("crashpad_snapshot_test_module_small") { ] } +if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + loadable_module("crashpad_snapshot_test_both_dt_hash_styles") { + testonly = true + sources = [ + "hash_types_test.cc", + ] + + # This makes `ld` emit both .hash and .gnu.hash sections. + ldflags = [ "-Wl,--hash-style=both" ] + } +} + if (crashpad_is_mac) { loadable_module("crashpad_snapshot_test_module_crashy_initializer") { testonly = true diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h b/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h index 208e98f275f..6116d4a1bbe 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h @@ -32,6 +32,12 @@ enum CPUArchitecture { //! \brief x86_64. kCPUArchitectureX86_64, + + //! \brief 32-bit ARM. + kCPUArchitectureARM, + + //! \brief 64-bit ARM. + kCPUArchitectureARM64 }; } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc index 5c964480a9b..8d83e638b34 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc @@ -164,6 +164,10 @@ uint64_t CPUContext::InstructionPointer() const { return x86->eip; case kCPUArchitectureX86_64: return x86_64->rip; + case kCPUArchitectureARM: + return arm->pc; + case kCPUArchitectureARM64: + return arm64->pc; default: NOTREACHED(); return ~0ull; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h index ba3ac18156a..3160b0eba05 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h @@ -18,6 +18,7 @@ #include <stdint.h> #include "snapshot/cpu_architecture.h" +#include "util/numeric/int128.h" namespace crashpad { @@ -258,6 +259,53 @@ struct CPUContextX86_64 { uint64_t dr7; }; +//! \brief A context structure carrying ARM CPU state. +struct CPUContextARM { + uint32_t regs[11]; + uint32_t fp; // r11 + uint32_t ip; // r12 + uint32_t sp; // r13 + uint32_t lr; // r14 + uint32_t pc; // r15 + uint32_t cpsr; + + struct { + struct fp_reg { + uint32_t sign1 : 1; + uint32_t unused : 15; + uint32_t sign2 : 1; + uint32_t exponent : 14; + uint32_t j : 1; + uint32_t mantissa1 : 31; + uint32_t mantisss0 : 32; + } fpregs[8]; + uint32_t fpsr : 32; + uint32_t fpcr : 32; + uint8_t type[8]; + uint32_t init_flag; + } fpa_regs; + + struct { + uint64_t vfp[32]; + uint32_t fpscr; + } vfp_regs; + + bool have_fpa_regs; + bool have_vfp_regs; +}; + +//! \brief A context structure carrying ARM64 CPU state. +struct CPUContextARM64 { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; + + uint128_struct fpsimd[32]; + uint32_t fpsr; + uint32_t fpcr; +}; + //! \brief A context structure capable of carrying the context of any supported //! CPU architecture. struct CPUContext { @@ -274,6 +322,8 @@ struct CPUContext { union { CPUContextX86* x86; CPUContextX86_64* x86_64; + CPUContextARM* arm; + CPUContextARM64* arm64; }; }; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_module.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_module.cc index 1f5a811e44b..d39fada38e8 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_module.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_module.cc @@ -86,7 +86,25 @@ __declspec(allocate("CPADinfo")) #else // !defined(OS_POSIX) && !defined(OS_WIN) #error Port #endif // !defined(OS_POSIX) && !defined(OS_WIN) -TestCrashpadInfo g_test_crashpad_info = {'CPad', sizeof(TestCrashpadInfo), 1}; +TestCrashpadInfo g_test_crashpad_info = {'CPad', + sizeof(TestCrashpadInfo), + 1, + 0, + 0, + 0, + 0, + 0, + 0, + nullptr, + nullptr, +#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) + nullptr, + nullptr, +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL +#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) + {} +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE +}; } // namespace } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc new file mode 100644 index 00000000000..ade9931bc46 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc @@ -0,0 +1,187 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/crashpad_types/crashpad_info_reader.h" + +#include <type_traits> + +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "util/linux/traits.h" +#include "util/misc/as_underlying_type.h" + +namespace crashpad { + +namespace { + +void UnsetIfNotValidTriState(TriState* value) { + switch (AsUnderlyingType(*value)) { + case AsUnderlyingType(TriState::kUnset): + case AsUnderlyingType(TriState::kEnabled): + case AsUnderlyingType(TriState::kDisabled): + return; + } + LOG(WARNING) << "Unsetting invalid TriState " << AsUnderlyingType(*value); + *value = TriState::kUnset; +} + +} // namespace + +class CrashpadInfoReader::InfoContainer { + public: + virtual ~InfoContainer() = default; + + virtual bool Read(const ProcessMemoryRange* memory, VMAddress address) = 0; + + protected: + InfoContainer() = default; +}; + +template <class Traits> +class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer { + public: + InfoContainerSpecific() : InfoContainer() {} + ~InfoContainerSpecific() override = default; + + bool Read(const ProcessMemoryRange* memory, VMAddress address) override { + if (!memory->Read(address, + offsetof(decltype(info), size) + sizeof(info.size), + &info)) { + return false; + } + + if (info.signature != CrashpadInfo::kSignature) { + LOG(ERROR) << "invalid signature 0x" << std::hex << info.signature; + return false; + } + + if (!memory->Read(address, + std::min(VMSize{info.size}, VMSize{sizeof(info)}), + &info)) { + return false; + } + + if (info.size > sizeof(info)) { + LOG(INFO) << "large crashpad info size " << info.size; + } + + if (info.version != 1) { + LOG(ERROR) << "unexpected version " << info.version; + return false; + } + + memset(reinterpret_cast<char*>(&info), 0, sizeof(info) - info.size); + + UnsetIfNotValidTriState(&info.crashpad_handler_behavior); + UnsetIfNotValidTriState(&info.system_crash_reporter_forwarding); + UnsetIfNotValidTriState(&info.gather_indirectly_referenced_memory); + + return true; + } + + struct { + uint32_t signature; + uint32_t size; + uint32_t version; + uint32_t indirectly_referenced_memory_cap; + uint32_t padding_0; + TriState crashpad_handler_behavior; + TriState system_crash_reporter_forwarding; + TriState gather_indirectly_referenced_memory; + uint8_t padding_1; + typename Traits::Address extra_memory_ranges; + typename Traits::Address simple_annotations; + typename Traits::Address user_data_minidump_stream_head; + typename Traits::Address annotations_list; + } info; + +#if defined(ARCH_CPU_64_BITS) +#define NATIVE_TRAITS Traits64 +#else +#define NATIVE_TRAITS Traits32 +#endif + static_assert(!std::is_same<Traits, NATIVE_TRAITS>::value || + sizeof(info) == sizeof(CrashpadInfo), + "CrashpadInfo size mismtach"); +#undef NATIVE_TRAITS +}; + +CrashpadInfoReader::CrashpadInfoReader() + : container_(), is_64_bit_(false), initialized_() {} + +CrashpadInfoReader::~CrashpadInfoReader() = default; + +bool CrashpadInfoReader::Initialize(const ProcessMemoryRange* memory, + VMAddress address) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + is_64_bit_ = memory->Is64Bit(); + + std::unique_ptr<InfoContainer> new_container; + if (is_64_bit_) { + new_container = std::make_unique<InfoContainerSpecific<Traits64>>(); + } else { + new_container = std::make_unique<InfoContainerSpecific<Traits32>>(); + } + + if (!new_container->Read(memory, address)) { + return false; + } + container_ = std::move(new_container); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +#define GET_MEMBER(name) \ + (is_64_bit_ \ + ? reinterpret_cast<InfoContainerSpecific<Traits64>*>(container_.get()) \ + ->info.name \ + : reinterpret_cast<InfoContainerSpecific<Traits32>*>(container_.get()) \ + ->info.name) + +#define DEFINE_GETTER(type, method, member) \ + type CrashpadInfoReader::method() { \ + INITIALIZATION_STATE_DCHECK_VALID(initialized_); \ + return GET_MEMBER(member); \ + } + +DEFINE_GETTER(TriState, CrashpadHandlerBehavior, crashpad_handler_behavior); + +DEFINE_GETTER(TriState, + SystemCrashReporterForwarding, + system_crash_reporter_forwarding); + +DEFINE_GETTER(TriState, + GatherIndirectlyReferencedMemory, + gather_indirectly_referenced_memory); + +DEFINE_GETTER(uint32_t, + IndirectlyReferencedMemoryCap, + indirectly_referenced_memory_cap); + +DEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges); + +DEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations); + +DEFINE_GETTER(VMAddress, AnnotationsList, annotations_list); + +DEFINE_GETTER(VMAddress, + UserDataMinidumpStreamHead, + user_data_minidump_stream_head); + +#undef DEFINE_GETTER +#undef GET_MEMBER + +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.h new file mode 100644 index 00000000000..5f2352efae0 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.h @@ -0,0 +1,75 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_ +#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_ + +#include <stdint.h> + +#include <memory> + +#include "base/macros.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/tri_state.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief Reads CrashpadInfo structs from another process via a +//! ProcessMemoryRange. +class CrashpadInfoReader { + public: + CrashpadInfoReader(); + ~CrashpadInfoReader(); + + //! \brief Initializes this object. + //! + //! This method must be successfully called bfore any other method in this + //! class. + //! + //! \param[in] memory The reader for the remote process. + //! \param[in] address The address in the remote process' address space of a + //! CrashpadInfo struct. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(const ProcessMemoryRange* memory, VMAddress address); + + //! \{ + //! \see CrashpadInfo + TriState CrashpadHandlerBehavior(); + TriState SystemCrashReporterForwarding(); + TriState GatherIndirectlyReferencedMemory(); + uint32_t IndirectlyReferencedMemoryCap(); + VMAddress ExtraMemoryRanges(); + VMAddress SimpleAnnotations(); + VMAddress AnnotationsList(); + VMAddress UserDataMinidumpStreamHead(); + //! \} + + private: + class InfoContainer; + + template <typename Traits> + class InfoContainerSpecific; + + std::unique_ptr<InfoContainer> container_; + bool is_64_bit_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(CrashpadInfoReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_ diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc new file mode 100644 index 00000000000..73564177ab5 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc @@ -0,0 +1,199 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/crashpad_types/crashpad_info_reader.h" + +#include <sys/types.h> +#include <unistd.h> + +#include "build/build_config.h" +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "client/simple_address_range_bag.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" +#include "test/process_type.h" +#include "util/file/file_io.h" +#include "util/misc/from_pointer_cast.h" +#include "util/process/process_memory_native.h" + +#if defined(OS_FUCHSIA) +#include <zircon/process.h> +#endif + +namespace crashpad { +namespace test { +namespace { + +constexpr TriState kCrashpadHandlerBehavior = TriState::kEnabled; +constexpr TriState kSystemCrashReporterForwarding = TriState::kDisabled; +constexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset; + +constexpr uint32_t kIndirectlyReferencedMemoryCap = 42; + +class CrashpadInfoTestDataSetup { + public: + CrashpadInfoTestDataSetup() { + CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo(); + + info->set_extra_memory_ranges(&extra_memory_); + info->set_simple_annotations(&simple_annotations_); + info->set_annotations_list(&annotation_list_); + info->set_crashpad_handler_behavior(kCrashpadHandlerBehavior); + info->set_system_crash_reporter_forwarding(kSystemCrashReporterForwarding); + info->set_gather_indirectly_referenced_memory( + kGatherIndirectlyReferencedMemory, kIndirectlyReferencedMemoryCap); + } + + void GetAddresses(VMAddress* info_address, + VMAddress* extra_memory_address, + VMAddress* simple_annotations_address, + VMAddress* annotations_list_address) { + *info_address = FromPointerCast<VMAddress>(CrashpadInfo::GetCrashpadInfo()); + *extra_memory_address = FromPointerCast<VMAddress>(&extra_memory_); + *simple_annotations_address = + FromPointerCast<VMAddress>(&simple_annotations_); + *annotations_list_address = FromPointerCast<VMAddress>(&annotation_list_); + } + + private: + SimpleAddressRangeBag extra_memory_; + SimpleStringDictionary simple_annotations_; + AnnotationList annotation_list_; + + DISALLOW_COPY_AND_ASSIGN(CrashpadInfoTestDataSetup); +}; + +void ExpectCrashpadInfo(ProcessType process, + bool is_64_bit, + VMAddress info_address, + VMAddress extra_memory_address, + VMAddress simple_annotations_address, + VMAddress annotations_list_address) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); + + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + + CrashpadInfoReader reader; + ASSERT_TRUE(reader.Initialize(&range, info_address)); + EXPECT_EQ(reader.CrashpadHandlerBehavior(), kCrashpadHandlerBehavior); + EXPECT_EQ(reader.SystemCrashReporterForwarding(), + kSystemCrashReporterForwarding); + EXPECT_EQ(reader.GatherIndirectlyReferencedMemory(), + kGatherIndirectlyReferencedMemory); + EXPECT_EQ(reader.IndirectlyReferencedMemoryCap(), + kIndirectlyReferencedMemoryCap); + EXPECT_EQ(reader.ExtraMemoryRanges(), extra_memory_address); + EXPECT_EQ(reader.SimpleAnnotations(), simple_annotations_address); + EXPECT_EQ(reader.AnnotationsList(), annotations_list_address); +} + +TEST(CrashpadInfoReader, ReadFromSelf) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + + CrashpadInfoTestDataSetup test_data_setup; + VMAddress info_address; + VMAddress extra_memory_address; + VMAddress simple_annotations_address; + VMAddress annotations_list_address; + test_data_setup.GetAddresses(&info_address, + &extra_memory_address, + &simple_annotations_address, + &annotations_list_address); + ExpectCrashpadInfo(GetSelfProcess(), + am_64_bit, + info_address, + extra_memory_address, + simple_annotations_address, + annotations_list_address); +} + +CRASHPAD_CHILD_TEST_MAIN(ReadFromChildTestMain) { + CrashpadInfoTestDataSetup test_data_setup; + VMAddress info_address; + VMAddress extra_memory_address; + VMAddress simple_annotations_address; + VMAddress annotations_list_address; + test_data_setup.GetAddresses(&info_address, + &extra_memory_address, + &simple_annotations_address, + &annotations_list_address); + + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + CheckedWriteFile(out, &info_address, sizeof(info_address)); + CheckedWriteFile(out, &extra_memory_address, sizeof(extra_memory_address)); + CheckedWriteFile( + out, &simple_annotations_address, sizeof(simple_annotations_address)); + CheckedWriteFile( + out, &annotations_list_address, sizeof(annotations_list_address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadFromChildTest : public MultiprocessExec { + public: + ReadFromChildTest() : MultiprocessExec() { + SetChildTestMainFunction("ReadFromChildTestMain"); + } + + ~ReadFromChildTest() = default; + + private: + void MultiprocessParent() { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + + VMAddress info_address; + VMAddress extra_memory_address; + VMAddress simple_annotations_address; + VMAddress annotations_list_address; + ASSERT_TRUE( + ReadFileExactly(ReadPipeHandle(), &info_address, sizeof(info_address))); + ASSERT_TRUE(ReadFileExactly( + ReadPipeHandle(), &extra_memory_address, sizeof(extra_memory_address))); + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), + &simple_annotations_address, + sizeof(simple_annotations_address))); + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), + &annotations_list_address, + sizeof(annotations_list_address))); + ExpectCrashpadInfo(ChildProcess(), + am_64_bit, + info_address, + extra_memory_address, + simple_annotations_address, + annotations_list_address); + } + + DISALLOW_COPY_AND_ASSIGN(ReadFromChildTest); +}; + +TEST(CrashpadInfoReader, ReadFromChild) { + ReadFromChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.cc new file mode 100644 index 00000000000..bd904979f19 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.cc @@ -0,0 +1,153 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/crashpad_types/image_annotation_reader.h" + +#include <string.h> +#include <sys/types.h> + +#include <algorithm> +#include <utility> + +#include "base/logging.h" +#include "build/build_config.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/simple_string_dictionary.h" +#include "snapshot/snapshot_constants.h" +#include "util/linux/traits.h" + +namespace crashpad { + +namespace process_types { + +template <class Traits> +struct Annotation { + typename Traits::Address link_node; + typename Traits::Address name; + typename Traits::Address value; + uint32_t size; + uint16_t type; +}; + +template <class Traits> +struct AnnotationList { + typename Traits::Address tail_pointer; + Annotation<Traits> head; + Annotation<Traits> tail; +}; + +} // namespace process_types + +#if defined(ARCH_CPU_64_BITS) +#define NATIVE_TRAITS Traits64 +#else +#define NATIVE_TRAITS Traits32 +#endif // ARCH_CPU_64_BITS + +static_assert(sizeof(process_types::Annotation<NATIVE_TRAITS>) == + sizeof(Annotation), + "Annotation size mismatch"); + +static_assert(sizeof(process_types::AnnotationList<NATIVE_TRAITS>) == + sizeof(AnnotationList), + "AnnotationList size mismatch"); + +#undef NATIVE_TRAITS + +ImageAnnotationReader::ImageAnnotationReader(const ProcessMemoryRange* memory) + : memory_(memory) {} + +ImageAnnotationReader::~ImageAnnotationReader() = default; + +bool ImageAnnotationReader::SimpleMap( + VMAddress address, + std::map<std::string, std::string>* annotations) const { + std::vector<SimpleStringDictionary::Entry> simple_annotations( + SimpleStringDictionary::num_entries); + + if (!memory_->Read(address, + simple_annotations.size() * sizeof(simple_annotations[0]), + &simple_annotations[0])) { + return false; + } + + for (const auto& entry : simple_annotations) { + size_t key_length = strnlen(entry.key, sizeof(entry.key)); + if (key_length) { + std::string key(entry.key, key_length); + std::string value(entry.value, strnlen(entry.value, sizeof(entry.value))); + if (!annotations->insert(std::make_pair(key, value)).second) { + LOG(WARNING) << "duplicate simple annotation " << key << " " << value; + } + } + } + return true; +} + +bool ImageAnnotationReader::AnnotationsList( + VMAddress address, + std::vector<AnnotationSnapshot>* annotations) const { + return memory_->Is64Bit() + ? ReadAnnotationList<Traits64>(address, annotations) + : ReadAnnotationList<Traits32>(address, annotations); +} + +template <class Traits> +bool ImageAnnotationReader::ReadAnnotationList( + VMAddress address, + std::vector<AnnotationSnapshot>* annotations) const { + process_types::AnnotationList<Traits> annotation_list; + if (!memory_->Read(address, sizeof(annotation_list), &annotation_list)) { + LOG(ERROR) << "could not read annotation list"; + return false; + } + + process_types::Annotation<Traits> current = annotation_list.head; + for (size_t index = 0; current.link_node != annotation_list.tail_pointer && + index < kMaxNumberOfAnnotations; + ++index) { + if (!memory_->Read(current.link_node, sizeof(current), ¤t)) { + LOG(ERROR) << "could not read annotation at index " << index; + return false; + } + + if (current.size == 0) { + continue; + } + + AnnotationSnapshot snapshot; + snapshot.type = current.type; + + if (!memory_->ReadCStringSizeLimited( + current.name, Annotation::kNameMaxLength, &snapshot.name)) { + LOG(WARNING) << "could not read annotation name at index " << index; + continue; + } + + size_t value_length = + std::min(static_cast<size_t>(current.size), Annotation::kValueMaxSize); + snapshot.value.resize(value_length); + if (!memory_->Read(current.value, value_length, snapshot.value.data())) { + LOG(WARNING) << "could not read annotation value at index " << index; + continue; + } + + annotations->push_back(std::move(snapshot)); + } + + return true; +} + +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.h new file mode 100644 index 00000000000..e425bef6d7d --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.h @@ -0,0 +1,76 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_ +#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "snapshot/annotation_snapshot.h" +#include "util/misc/address_types.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief Reads Annotations from another process via a ProcessMemoryRange. +//! +//! These annotations are stored for the benefit of crash reporters, and provide +//! information thought to be potentially useful for crash analysis. +class ImageAnnotationReader { + public: + //! \brief Constructs the object. + //! + //! \param[in] memory A memory reader for the remote process. + explicit ImageAnnotationReader(const ProcessMemoryRange* memory); + + ~ImageAnnotationReader(); + + //! \brief Reads annotations that are organized as key-value pairs, where all + //! keys and values are strings. + //! + //! \param[in] address The address in the target process' address space of a + //! SimpleStringDictionary containing the annotations to read. + //! \param[out] annotations The annotations read, valid if this method + //! returns `true`. + //! \return `true` on success. `false` on failure with a message logged. + bool SimpleMap(VMAddress address, + std::map<std::string, std::string>* annotations) const; + + //! \brief Reads the module's annotations that are organized as a list of + //! typed annotation objects. + //! + //! \param[in] address The address in the target process' address space of an + //! AnnotationList. + //! \param[out] annotations The annotations read, valid if this method returns + //! `true`. + //! \return `true` on success. `false` on failure with a message logged. + bool AnnotationsList(VMAddress, + std::vector<AnnotationSnapshot>* annotations) const; + + private: + template <class Traits> + bool ReadAnnotationList(VMAddress address, + std::vector<AnnotationSnapshot>* annotations) const; + + const ProcessMemoryRange* memory_; // weak + + DISALLOW_COPY_AND_ASSIGN(ImageAnnotationReader); +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_ diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc new file mode 100644 index 00000000000..b0e635ff75b --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc @@ -0,0 +1,164 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/crashpad_types/image_annotation_reader.h" + +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include <algorithm> + +#include "base/logging.h" +#include "build/build_config.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "test/multiprocess.h" +#include "util/file/file_io.h" +#include "util/misc/as_underlying_type.h" +#include "util/misc/from_pointer_cast.h" +#include "util/process/process_memory_linux.h" + +namespace crashpad { +namespace test { +namespace { + +void ExpectSimpleMap(const std::map<std::string, std::string>& map, + const SimpleStringDictionary& expected_map) { + EXPECT_EQ(map.size(), expected_map.GetCount()); + for (const auto& pair : map) { + EXPECT_EQ(pair.second, expected_map.GetValueForKey(pair.first)); + } +} + +void ExpectAnnotationList(const std::vector<AnnotationSnapshot>& list, + AnnotationList& expected_list) { + size_t index = 0; + for (const Annotation* expected_annotation : expected_list) { + const AnnotationSnapshot& annotation = list[index++]; + EXPECT_EQ(annotation.name, expected_annotation->name()); + EXPECT_EQ(annotation.type, AsUnderlyingType(expected_annotation->type())); + EXPECT_EQ(annotation.value.size(), expected_annotation->size()); + EXPECT_EQ(memcmp(annotation.value.data(), + expected_annotation->value(), + std::min(VMSize{annotation.value.size()}, + VMSize{expected_annotation->size()})), + 0); + } +} + +class AnnotationTest { + public: + AnnotationTest() + : expected_simple_map_(), + test_annotations_(), + expected_annotation_list_() { + expected_simple_map_.SetKeyValue("key", "value"); + expected_simple_map_.SetKeyValue("key2", "value2"); + + static constexpr char kAnnotationName[] = "test annotation"; + static constexpr char kAnnotationValue[] = "test annotation value"; + test_annotations_.push_back(std::make_unique<Annotation>( + Annotation::Type::kString, + kAnnotationName, + reinterpret_cast<void*>(const_cast<char*>(kAnnotationValue)))); + test_annotations_.back()->SetSize(sizeof(kAnnotationValue)); + expected_annotation_list_.Add(test_annotations_.back().get()); + + static constexpr char kAnnotationName2[] = "test annotation2"; + static constexpr char kAnnotationValue2[] = "test annotation value2"; + test_annotations_.push_back(std::make_unique<Annotation>( + Annotation::Type::kString, + kAnnotationName2, + reinterpret_cast<void*>(const_cast<char*>(kAnnotationValue2)))); + test_annotations_.back()->SetSize(sizeof(kAnnotationValue2)); + expected_annotation_list_.Add(test_annotations_.back().get()); + } + + ~AnnotationTest() = default; + + void ExpectAnnotations(pid_t pid, bool is_64_bit) { + ProcessMemoryLinux memory; + ASSERT_TRUE(memory.Initialize(pid)); + + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + + ImageAnnotationReader reader(&range); + + std::map<std::string, std::string> simple_map; + ASSERT_TRUE(reader.SimpleMap( + FromPointerCast<VMAddress>(&expected_simple_map_), &simple_map)); + ExpectSimpleMap(simple_map, expected_simple_map_); + + std::vector<AnnotationSnapshot> annotation_list; + ASSERT_TRUE(reader.AnnotationsList( + FromPointerCast<VMAddress>(&expected_annotation_list_), + &annotation_list)); + ExpectAnnotationList(annotation_list, expected_annotation_list_); + } + + private: + SimpleStringDictionary expected_simple_map_; + std::vector<std::unique_ptr<Annotation>> test_annotations_; + AnnotationList expected_annotation_list_; + + DISALLOW_COPY_AND_ASSIGN(AnnotationTest); +}; + +TEST(ImageAnnotationReader, ReadFromSelf) { + AnnotationTest test; + +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + + test.ExpectAnnotations(getpid(), am_64_bit); +} + +class ReadFromChildTest : public Multiprocess { + public: + ReadFromChildTest() : Multiprocess(), annotation_test_() {} + + ~ReadFromChildTest() {} + + private: + void MultiprocessParent() { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + annotation_test_.ExpectAnnotations(ChildPID(), am_64_bit); + } + + void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } + + AnnotationTest annotation_test_; + + DISALLOW_COPY_AND_ASSIGN(ReadFromChildTest); +}; + +TEST(ImageAnnotationReader, ReadFromChild) { + ReadFromChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.cc index 681b1b570ea..9a44e12eaa1 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.cc @@ -16,6 +16,8 @@ #include <elf.h> +#include <type_traits> + #include "util/stdlib/map_insert.h" namespace crashpad { @@ -48,8 +50,14 @@ bool Read(const ProcessMemoryRange& memory, // Skip these entries for now. break; default: + static_assert(std::is_unsigned<decltype(entry.d_un.d_ptr)>::value, + "type must be unsigned"); + static_assert(static_cast<void*>(&entry.d_un.d_ptr) == + static_cast<void*>(&entry.d_un.d_val) && + sizeof(entry.d_un.d_ptr) == sizeof(entry.d_un.d_val), + "d_ptr and d_val must be aliases"); if (!MapInsertOrReplace( - &local_values, entry.d_tag, entry.d_un.d_val, nullptr)) { + &local_values, entry.d_tag, entry.d_un.d_ptr, nullptr)) { LOG(ERROR) << "duplicate dynamic array entry"; return false; } diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h index 36628a0b854..e80a8cbc556 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h @@ -50,13 +50,15 @@ class ElfDynamicArrayReader { //! //! \param[in] tag Specifies which value should be retrieved. The possible //! values for this parameter are the `DT_*` values from `<elf.h>`. + //! \param[in] log Specifies whether an error should be logged if \a tag is + //! not found. //! \param[out] value The value, casted to an appropriate type, if found. //! \return `true` if the value is found. template <typename V> - bool GetValue(uint64_t tag, V* value) { + bool GetValue(uint64_t tag, bool log, V* value) { auto iter = values_.find(tag); if (iter == values_.end()) { - LOG(ERROR) << "tag not found"; + LOG_IF(ERROR, log) << "tag not found"; return false; } return ReinterpretBytes(iter->second, value); diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc index b7c9a5f4954..2fc960892a5 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc @@ -519,8 +519,8 @@ bool ElfImageReader::ReadDynamicStringTableAtOffset(VMSize offset, VMAddress string_table_address; VMSize string_table_size; - if (!GetAddressFromDynamicArray(DT_STRTAB, &string_table_address) || - !dynamic_array_->GetValue(DT_STRSZ, &string_table_size)) { + if (!GetAddressFromDynamicArray(DT_STRTAB, true, &string_table_address) || + !dynamic_array_->GetValue(DT_STRSZ, true, &string_table_size)) { LOG(ERROR) << "missing string table info"; return false; } @@ -542,7 +542,7 @@ bool ElfImageReader::GetDebugAddress(VMAddress* debug) { if (!InitializeDynamicArray()) { return false; } - return GetAddressFromDynamicArray(DT_DEBUG, debug); + return GetAddressFromDynamicArray(DT_DEBUG, true, debug); } bool ElfImageReader::InitializeProgramHeaders() { @@ -609,25 +609,40 @@ bool ElfImageReader::InitializeDynamicSymbolTable() { } VMAddress symbol_table_address; - if (!GetAddressFromDynamicArray(DT_SYMTAB, &symbol_table_address)) { + if (!GetAddressFromDynamicArray(DT_SYMTAB, true, &symbol_table_address)) { LOG(ERROR) << "no symbol table"; return false; } - symbol_table_.reset( - new ElfSymbolTableReader(&memory_, this, symbol_table_address)); + // Try both DT_HASH and DT_GNU_HASH. They're completely different, but both + // circuitously offer a way to find the number of entries in the symbol table. + // DT_HASH is specifically checked first, because depending on the linker, the + // count maybe be incorrect for zero-export cases. In practice, it is believed + // that the zero-export case is probably not particularly useful, so this + // incorrect count will only occur in constructed test cases (see + // ElfImageReader.DtHashAndDtGnuHashMatch). + VMSize number_of_symbol_table_entries; + if (!GetNumberOfSymbolEntriesFromDtHash(&number_of_symbol_table_entries) && + !GetNumberOfSymbolEntriesFromDtGnuHash(&number_of_symbol_table_entries)) { + LOG(ERROR) << "could not retrieve number of symbol table entries"; + return false; + } + + symbol_table_.reset(new ElfSymbolTableReader( + &memory_, this, symbol_table_address, number_of_symbol_table_entries)); symbol_table_initialized_.set_valid(); return true; } bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag, + bool log, VMAddress* address) { - if (!dynamic_array_->GetValue(tag, address)) { + if (!dynamic_array_->GetValue(tag, log, address)) { return false; } -#if defined(OS_ANDROID) - // The GNU loader updates the dynamic array according to the load bias while - // the Android loader only updates the debug address. +#if defined(OS_ANDROID) || defined(OS_FUCHSIA) + // The GNU loader updates the dynamic array according to the load bias. + // The Android and Fuchsia loaders only update the debug address. if (tag != DT_DEBUG) { *address += GetLoadBias(); } @@ -635,6 +650,100 @@ bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag, return true; } +bool ElfImageReader::GetNumberOfSymbolEntriesFromDtHash( + VMSize* number_of_symbol_table_entries) { + if (!InitializeDynamicArray()) { + return false; + } + + VMAddress dt_hash_address; + if (!GetAddressFromDynamicArray(DT_HASH, false, &dt_hash_address)) { + return false; + } + + struct { + uint32_t nbucket; + uint32_t nchain; + } header; + + if (!memory_.Read(dt_hash_address, sizeof(header), &header)) { + LOG(ERROR) << "failed to read DT_HASH header"; + return false; + } + + *number_of_symbol_table_entries = header.nchain; + return true; +} + +bool ElfImageReader::GetNumberOfSymbolEntriesFromDtGnuHash( + VMSize* number_of_symbol_table_entries) { + if (!InitializeDynamicArray()) { + return false; + } + + VMAddress dt_gnu_hash_address; + if (!GetAddressFromDynamicArray(DT_GNU_HASH, false, &dt_gnu_hash_address)) { + return false; + } + + // See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ and + // https://sourceware.org/ml/binutils/2006-10/msg00377.html. + struct { + uint32_t nbuckets; + uint32_t symoffset; + uint32_t bloom_size; + uint32_t bloom_shift; + } header; + if (!memory_.Read(dt_gnu_hash_address, sizeof(header), &header)) { + LOG(ERROR) << "failed to read DT_GNU_HASH header"; + return false; + } + + std::vector<uint32_t> buckets(header.nbuckets); + const size_t kNumBytesForBuckets = sizeof(buckets[0]) * buckets.size(); + const size_t kWordSize = + memory_.Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t); + const VMAddress buckets_address = + dt_gnu_hash_address + sizeof(header) + (kWordSize * header.bloom_size); + if (!memory_.Read(buckets_address, kNumBytesForBuckets, buckets.data())) { + LOG(ERROR) << "read buckets"; + return false; + } + + // Locate the chain that handles the largest index bucket. + uint32_t last_symbol = 0; + for (uint32_t i = 0; i < header.nbuckets; ++i) { + last_symbol = std::max(buckets[i], last_symbol); + } + + if (last_symbol < header.symoffset) { + *number_of_symbol_table_entries = header.symoffset; + return true; + } + + // Walk the bucket's chain to add the chain length to the total. + const VMAddress chains_base_address = buckets_address + kNumBytesForBuckets; + for (;;) { + uint32_t chain_entry; + if (!memory_.Read(chains_base_address + (last_symbol - header.symoffset) * + sizeof(chain_entry), + sizeof(chain_entry), + &chain_entry)) { + LOG(ERROR) << "read chain entry"; + return false; + } + + ++last_symbol; + + // If the low bit is set, this entry is the end of the chain. + if (chain_entry & 1) + break; + } + + *number_of_symbol_table_entries = last_symbol; + return true; +} + std::unique_ptr<ElfImageReader::NoteReader> ElfImageReader::Notes( ssize_t max_note_size) { return std::make_unique<NoteReader>( @@ -649,4 +758,8 @@ ElfImageReader::NotesWithNameAndType(const std::string& name, this, &memory_, program_headers_.get(), max_note_size, name, type, true); } +const ProcessMemoryRange* ElfImageReader::Memory() const { + return &memory_; +} + } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.h index b6b5997983b..7549f34679f 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.h @@ -202,6 +202,38 @@ class ElfImageReader { NoteReader::NoteType type, ssize_t max_note_size); + //! \brief Return a ProcessMemoryRange restricted to the range of this image. + //! + //! The caller does not take ownership of the returned object. + const ProcessMemoryRange* Memory() const; + + //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB` + //! according to the data in the `DT_HASH` section. + //! + //! \note Exposed for testing, not normally otherwise useful. + //! + //! \param[out] number_of_symbol_table_entries The number of entries expected + //! in `DT_SYMTAB`. + //! \return `true` if a `DT_HASH` section was found, and was read + //! successfully, otherwise `false` with an error logged. + bool GetNumberOfSymbolEntriesFromDtHash( + VMSize* number_of_symbol_table_entries); + + //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB` + //! according to the data in the `DT_GNU_HASH` section. + //! + //! \note Exposed for testing, not normally otherwise useful. + //! + //! \note Depending on the linker that generated the `DT_GNU_HASH` section, + //! this value may not be as expected if there are zero exported symbols. + //! + //! \param[out] number_of_symbol_table_entries The number of entries expected + //! in `DT_SYMTAB`. + //! \return `true` if a `DT_GNU_HASH` section was found, and was read + //! successfully, otherwise `false` with an error logged. + bool GetNumberOfSymbolEntriesFromDtGnuHash( + VMSize* number_of_symbol_table_entries); + private: template <typename PhdrType> class ProgramHeaderTableSpecific; @@ -209,7 +241,7 @@ class ElfImageReader { bool InitializeProgramHeaders(); bool InitializeDynamicArray(); bool InitializeDynamicSymbolTable(); - bool GetAddressFromDynamicArray(uint64_t tag, VMAddress* address); + bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address); union { Elf32_Ehdr header_32_; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader_test.cc index e9572be968c..dc13ad6f913 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader_test.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader_test.cc @@ -15,18 +15,37 @@ #include "snapshot/elf/elf_image_reader.h" #include <dlfcn.h> +#include <link.h> #include <unistd.h> #include "base/logging.h" #include "build/build_config.h" #include "gtest/gtest.h" -#include "test/multiprocess.h" +#include "test/multiprocess_exec.h" +#include "test/process_type.h" +#include "test/scoped_module_handle.h" +#include "test/test_paths.h" #include "util/file/file_io.h" -#include "util/linux/auxiliary_vector.h" -#include "util/linux/memory_map.h" #include "util/misc/address_types.h" #include "util/misc/from_pointer_cast.h" -#include "util/process/process_memory_linux.h" +#include "util/process/process_memory_native.h" + +#if defined(OS_FUCHSIA) + +#include <zircon/syscalls.h> + +#include "base/fuchsia/fuchsia_logging.h" + +#elif defined(OS_LINUX) || defined(OS_ANDROID) + +#include "util/linux/auxiliary_vector.h" +#include "util/linux/memory_map.h" + +#else + +#error Port. + +#endif // OS_FUCHSIA extern "C" { __attribute__((visibility("default"))) void @@ -37,15 +56,51 @@ namespace crashpad { namespace test { namespace { -void LocateExecutable(pid_t pid, bool is_64_bit, VMAddress* elf_address) { + +#if defined(OS_FUCHSIA) + +void LocateExecutable(ProcessType process, + ProcessMemory* memory, + bool is_64_bit, + VMAddress* elf_address) { + uintptr_t debug_address; + zx_status_t status = zx_object_get_property(process, + ZX_PROP_PROCESS_DEBUG_ADDR, + &debug_address, + sizeof(debug_address)); + ASSERT_EQ(status, ZX_OK) + << "zx_object_get_property: ZX_PROP_PROCESS_DEBUG_ADDR"; + // Can be 0 if requested before the loader has loaded anything. + EXPECT_NE(debug_address, 0u); + + constexpr auto k_r_debug_map_offset = offsetof(r_debug, r_map); + uintptr_t map; + ASSERT_TRUE( + memory->Read(debug_address + k_r_debug_map_offset, sizeof(map), &map)) + << "read link_map"; + + constexpr auto k_link_map_addr_offset = offsetof(link_map, l_addr); + uintptr_t base; + ASSERT_TRUE(memory->Read(map + k_link_map_addr_offset, sizeof(base), &base)) + << "read base"; + + *elf_address = base; +} + +#elif defined(OS_LINUX) || defined(OS_ANDROID) + +void LocateExecutable(ProcessType process, + ProcessMemory* memory, + bool is_64_bit, + VMAddress* elf_address) { AuxiliaryVector aux; - ASSERT_TRUE(aux.Initialize(pid, is_64_bit)); + ASSERT_TRUE(aux.Initialize(process, is_64_bit)); VMAddress phdrs; ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); MemoryMap memory_map; - ASSERT_TRUE(memory_map.Initialize(pid)); + ASSERT_TRUE(memory_map.Initialize(process)); const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs); ASSERT_TRUE(phdr_mapping); const MemoryMap::Mapping* exe_mapping = @@ -54,6 +109,8 @@ void LocateExecutable(pid_t pid, bool is_64_bit, VMAddress* elf_address) { *elf_address = exe_mapping->range.Base(); } +#endif // OS_FUCHSIA + void ExpectSymbol(ElfImageReader* reader, const std::string& symbol_name, VMAddress expected_symbol_address) { @@ -67,27 +124,28 @@ void ExpectSymbol(ElfImageReader* reader, reader->GetDynamicSymbol("notasymbol", &symbol_address, &symbol_size)); } -void ReadThisExecutableInTarget(pid_t pid) { +void ReadThisExecutableInTarget(ProcessType process, + VMAddress exported_symbol_address) { #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; #else constexpr bool am_64_bit = false; #endif // ARCH_CPU_64_BITS - VMAddress elf_address; - LocateExecutable(pid, am_64_bit, &elf_address); - - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); ProcessMemoryRange range; ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); + VMAddress elf_address; + LocateExecutable(process, &memory, am_64_bit, &elf_address); + ASSERT_NO_FATAL_FAILURE(); + ElfImageReader reader; ASSERT_TRUE(reader.Initialize(range, elf_address)); - ExpectSymbol(&reader, - "ElfImageReaderTestExportedSymbol", - FromPointerCast<VMAddress>(ElfImageReaderTestExportedSymbol)); + ExpectSymbol( + &reader, "ElfImageReaderTestExportedSymbol", exported_symbol_address); ElfImageReader::NoteReader::Result result; std::string note_name; @@ -121,69 +179,174 @@ void ReadThisExecutableInTarget(pid_t pid) { ElfImageReader::NoteReader::Result::kNoMoreNotes); } -// Assumes that libc is loaded at the same address in this process as in the -// target, which it is for the fork test below. -void ReadLibcInTarget(pid_t pid) { +void ReadLibcInTarget(ProcessType process, + VMAddress elf_address, + VMAddress getpid_address) { #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; #else constexpr bool am_64_bit = false; #endif // ARCH_CPU_64_BITS - Dl_info info; - ASSERT_TRUE(dladdr(reinterpret_cast<void*>(getpid), &info)) << "dladdr:" - << dlerror(); - VMAddress elf_address = FromPointerCast<VMAddress>(info.dli_fbase); - - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); ProcessMemoryRange range; ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); ElfImageReader reader; ASSERT_TRUE(reader.Initialize(range, elf_address)); - ExpectSymbol(&reader, "getpid", FromPointerCast<VMAddress>(getpid)); + ExpectSymbol(&reader, "getpid", getpid_address); +} + +TEST(ElfImageReader, MainExecutableSelf) { + ReadThisExecutableInTarget( + GetSelfProcess(), + FromPointerCast<VMAddress>(ElfImageReaderTestExportedSymbol)); +} + +CRASHPAD_CHILD_TEST_MAIN(ReadExecutableChild) { + VMAddress exported_symbol_address = + FromPointerCast<VMAddress>(ElfImageReaderTestExportedSymbol); + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &exported_symbol_address, + sizeof(exported_symbol_address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; } -class ReadExecutableChildTest : public Multiprocess { +class ReadExecutableChildTest : public MultiprocessExec { public: - ReadExecutableChildTest() : Multiprocess() {} - ~ReadExecutableChildTest() {} + ReadExecutableChildTest() : MultiprocessExec() {} private: - void MultiprocessParent() { ReadThisExecutableInTarget(ChildPID()); } - void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } + void MultiprocessParent() { + // This read serves two purposes -- on Fuchsia, the loader may have not + // filled in debug address as soon as the child process handle is valid, so + // this causes a wait at least until the main() of the child, at which point + // it will always be valid. Secondarily, the address of the symbol to be + // looked up needs to be communicated. + VMAddress exported_symbol_address; + CheckedReadFileExactly(ReadPipeHandle(), + &exported_symbol_address, + sizeof(exported_symbol_address)); + ReadThisExecutableInTarget(ChildProcess(), exported_symbol_address); + } }; -TEST(ElfImageReader, MainExecutableSelf) { - ReadThisExecutableInTarget(getpid()); -} - TEST(ElfImageReader, MainExecutableChild) { ReadExecutableChildTest test; + test.SetChildTestMainFunction("ReadExecutableChild"); test.Run(); } TEST(ElfImageReader, OneModuleSelf) { - ReadLibcInTarget(getpid()); + Dl_info info; + ASSERT_TRUE(dladdr(reinterpret_cast<void*>(getpid), &info)) << "dladdr:" + << dlerror(); + VMAddress elf_address = FromPointerCast<VMAddress>(info.dli_fbase); + ReadLibcInTarget( + GetSelfProcess(), elf_address, FromPointerCast<VMAddress>(getpid)); } -class ReadLibcChildTest : public Multiprocess { +CRASHPAD_CHILD_TEST_MAIN(ReadLibcChild) { + // Get the address of libc (by using getpid() as a representative member), + // and also the address of getpid() itself, and write them to the parent, so + // it can validate reading this information back out. + Dl_info info; + EXPECT_TRUE(dladdr(reinterpret_cast<void*>(getpid), &info)) + << "dladdr:" << dlerror(); + VMAddress elf_address = FromPointerCast<VMAddress>(info.dli_fbase); + VMAddress getpid_address = FromPointerCast<VMAddress>(getpid); + + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &elf_address, + sizeof(elf_address)); + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &getpid_address, + sizeof(getpid_address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadLibcChildTest : public MultiprocessExec { public: - ReadLibcChildTest() : Multiprocess() {} + ReadLibcChildTest() : MultiprocessExec() {} ~ReadLibcChildTest() {} private: - void MultiprocessParent() { ReadLibcInTarget(ChildPID()); } - void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } + void MultiprocessParent() { + VMAddress elf_address, getpid_address; + CheckedReadFileExactly(ReadPipeHandle(), &elf_address, sizeof(elf_address)); + CheckedReadFileExactly( + ReadPipeHandle(), &getpid_address, sizeof(getpid_address)); + ReadLibcInTarget(ChildProcess(), elf_address, getpid_address); + } }; TEST(ElfImageReader, OneModuleChild) { ReadLibcChildTest test; + test.SetChildTestMainFunction("ReadLibcChild"); test.Run(); } +#if defined(OS_FUCHSIA) + +// crashpad_snapshot_test_both_dt_hash_styles is specially built and forced to +// include both .hash and .gnu.hash sections. Linux, Android, and Fuchsia have +// different defaults for which of these sections should be included; this test +// confirms that we get the same count from both sections. +// +// TODO(scottmg): Investigation in https://crrev.com/c/876879 resulted in +// realizing that ld.bfd does not emit a .gnu.hash that is very useful for this +// purpose when there's 0 exported entries in the module. This is not likely to +// be too important, as there's little need to look up non-exported symbols. +// However, it makes this test not work on Linux, where the default build uses +// ld.bfd. On Fuchsia, the only linker in use is lld, and it generates the +// expected .gnu.hash. So, for now, this test is only run on Fuchsia, not Linux. +// +// TODO(scottmg): Separately, the location of the ELF on Android needs some +// work, and then the test could also be enabled there. +TEST(ElfImageReader, DtHashAndDtGnuHashMatch) { + base::FilePath module_path = + TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), + FILE_PATH_LITERAL("both_dt_hash_styles"), + TestPaths::FileType::kLoadableModule); + // TODO(scottmg): Remove this when upstream Fuchsia bug ZX-1619 is resolved. + // See also explanation in build/run_tests.py for Fuchsia .so files. + module_path = module_path.BaseName(); + ScopedModuleHandle module( + dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); + ASSERT_TRUE(module.valid()) << "dlopen " << module_path.value() << ": " + << dlerror(); + +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(GetSelfProcess())); + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); + + struct link_map* lm = reinterpret_cast<struct link_map*>(module.get()); + + ElfImageReader reader; + ASSERT_TRUE(reader.Initialize(range, lm->l_addr)); + + VMSize from_dt_hash; + ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtHash(&from_dt_hash)); + + VMSize from_dt_gnu_hash; + ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtGnuHash(&from_dt_gnu_hash)); + + EXPECT_EQ(from_dt_hash, from_dt_gnu_hash); +} + +#endif // OS_FUCHSIA + } // namespace } // namespace test } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.cc index c6395db75f0..c2c6abf0a81 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.cc @@ -51,8 +51,12 @@ uint8_t GetVisibility(const Elf64_Sym& sym) { ElfSymbolTableReader::ElfSymbolTableReader(const ProcessMemoryRange* memory, ElfImageReader* elf_reader, - VMAddress address) - : memory_(memory), elf_reader_(elf_reader), base_address_(address) {} + VMAddress address, + VMSize num_entries) + : memory_(memory), + elf_reader_(elf_reader), + base_address_(address), + num_entries_(num_entries) {} ElfSymbolTableReader::~ElfSymbolTableReader() {} @@ -68,9 +72,10 @@ bool ElfSymbolTableReader::ScanSymbolTable(const std::string& name, VMAddress address = base_address_; SymEnt entry; std::string string; - while (memory_->Read(address, sizeof(entry), &entry) && - elf_reader_->ReadDynamicStringTableAtOffset(entry.st_name, &string)) { - if (string == name) { + size_t i = 0; + while (i < num_entries_ && memory_->Read(address, sizeof(entry), &entry)) { + if (elf_reader_->ReadDynamicStringTableAtOffset(entry.st_name, &string) && + string == name) { info_out->address = entry.st_value; info_out->size = entry.st_size; info_out->shndx = entry.st_shndx; @@ -79,7 +84,9 @@ bool ElfSymbolTableReader::ScanSymbolTable(const std::string& name, info_out->visibility = GetVisibility(entry); return true; } + // TODO(scottmg): This should respect DT_SYMENT if present. address += sizeof(entry); + ++i; } return false; } diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.h index 880915a2b4e..15930372cad 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.h @@ -61,7 +61,8 @@ class ElfSymbolTableReader { // lookup. ElfSymbolTableReader(const ProcessMemoryRange* memory, ElfImageReader* elf_reader, - VMAddress address); + VMAddress address, + VMSize num_entries); ~ElfSymbolTableReader(); //! \brief Lookup information about a symbol. @@ -78,6 +79,7 @@ class ElfSymbolTableReader { const ProcessMemoryRange* const memory_; // weak ElfImageReader* const elf_reader_; // weak const VMAddress base_address_; + const VMSize num_entries_; DISALLOW_COPY_AND_ASSIGN(ElfSymbolTableReader); }; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/hash_types_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/hash_types_test.cc new file mode 100644 index 00000000000..436323b207c --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/hash_types_test.cc @@ -0,0 +1,17 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +int main() { + return 0; +} diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.cc index 26c65be1a8a..1a6589f8e99 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.cc @@ -151,9 +151,92 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context, context->dr6 = 0; context->dr7 = 0; } -#else -#error Port. -#endif // ARCH_CPU_X86_FAMILY || DOXYGEN + +#elif defined(ARCH_CPU_ARM_FAMILY) + +void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextARM* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "registers size mismatch"); + memcpy(&context->regs, &thread_context.regs, sizeof(context->regs)); + context->fp = thread_context.fp; + context->ip = thread_context.ip; + context->sp = thread_context.sp; + context->lr = thread_context.lr; + context->pc = thread_context.pc; + context->cpsr = thread_context.cpsr; + + static_assert(sizeof(context->vfp_regs) == sizeof(float_context.vfp), + "vfp size mismatch"); + context->have_vfp_regs = float_context.have_vfp; + if (float_context.have_vfp) { + memcpy(&context->vfp_regs, &float_context.vfp, sizeof(context->vfp_regs)); + } + + static_assert(sizeof(context->fpa_regs) == sizeof(float_context.fpregs), + "fpregs size mismatch"); + context->have_fpa_regs = float_context.have_fpregs; + if (float_context.have_fpregs) { + memcpy( + &context->fpa_regs, &float_context.fpregs, sizeof(context->fpa_regs)); + } +} + +void InitializeCPUContextARM_NoFloatingPoint( + const SignalThreadContext32& thread_context, + CPUContextARM* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "registers size mismatch"); + memcpy(&context->regs, &thread_context.regs, sizeof(context->regs)); + context->fp = thread_context.fp; + context->ip = thread_context.ip; + context->sp = thread_context.sp; + context->lr = thread_context.lr; + context->pc = thread_context.pc; + context->cpsr = thread_context.cpsr; +} + +void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextARM64* context) { + InitializeCPUContextARM64_NoFloatingPoint(thread_context, context); + + static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs), + "fpsimd context size mismatch"); + memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd)); + context->fpsr = float_context.fpsr; + context->fpcr = float_context.fpcr; +} + +void InitializeCPUContextARM64_NoFloatingPoint( + const ThreadContext::t64_t& thread_context, + CPUContextARM64* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "gpr context size mismtach"); + memcpy(context->regs, thread_context.regs, sizeof(context->regs)); + context->sp = thread_context.sp; + context->pc = thread_context.pc; + context->pstate = thread_context.pstate; +} + +void InitializeCPUContextARM64_OnlyFPSIMD( + const SignalFPSIMDContext& float_context, + CPUContextARM64* context) { + static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs), + "fpsimd context size mismatch"); + memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd)); + context->fpsr = float_context.fpsr; + context->fpcr = float_context.fpcr; +} + +void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context) { + memset(context->fpsimd, 0, sizeof(context->fpsimd)); + context->fpsr = 0; + context->fpcr = 0; +} + +#endif // ARCH_CPU_X86_FAMILY } // namespace internal } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.h index 092762c203b..1ec4a1b4d42 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.h @@ -68,10 +68,73 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context, const SignalFloatContext64& float_context, CPUContextX86_64* context); //! \} -#else -#error Port. // TODO(jperaza): ARM + #endif // ARCH_CPU_X86_FAMILY || DOXYGEN +#if defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN + +//! \brief Initializes a CPUContextARM structure from native context structures +//! on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextARM structure to initialize. +void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextARM* context); + +//! \brief Initializes GPR state in a CPUContextARM from a native signal context +//! structure on Linux. +//! +//! Floating point state is not initialized. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextARM structure to initialize. +void InitializeCPUContextARM_NoFloatingPoint( + const SignalThreadContext32& thread_context, + CPUContextARM* context); + +//! \brief Initializes a CPUContextARM64 structure from native context +//! structures on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextARM64* context); + +//! \brief Initializes GPR state in a CPUContextARM64 from a native context +//! structure on Linux. +//! +//! Floating point state is not initialized. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64_NoFloatingPoint( + const ThreadContext::t64_t& thread_context, + CPUContextARM64* context); + +//! \brief Initializes FPSIMD state in a CPUContextARM64 from a native fpsimd +//! signal context structure on Linux. +//! +//! General purpose registers are not initialized. +//! +//! \param[in] float_context The native fpsimd context. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64_OnlyFPSIMD( + const SignalFPSIMDContext& float_context, + CPUContextARM64* context); + +//! \brief Initializes FPSIMD state in a CPUContextARM64 to zero. +//! +//! General purpose registers are not initialized. +//! +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context); + +#endif // ARCH_CPU_ARM_FAMILY || DOXYGEN + } // namespace internal } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.cc index 1fcbced4589..498b1f79413 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.cc @@ -67,7 +67,10 @@ bool ExceptionSnapshotLinux::ReadContext<ContextTraits32>( context_.x86); } else { - DCHECK_EQ(ucontext.fprs.magic, 0xffff); + if (ucontext.fprs.magic != 0xffff) { + LOG(ERROR) << "unexpected magic 0x" << std::hex << ucontext.fprs.magic; + return false; + } InitializeCPUContextX86( ucontext.mcontext.gprs, ucontext.fprs, context_.x86); } @@ -91,6 +94,163 @@ bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>( ucontext.mcontext.gprs, ucontext.fprs, context_.x86_64); return true; } + +#elif defined(ARCH_CPU_ARM_FAMILY) + +template <> +bool ExceptionSnapshotLinux::ReadContext<ContextTraits32>( + ProcessReader* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureARM; + context_.arm = &context_union_.arm; + + CPUContextARM* dest_context = context_.arm; + ProcessMemory* memory = reader->Memory(); + + LinuxVMAddress gprs_address = + context_address + offsetof(UContext<ContextTraits32>, mcontext32) + + offsetof(MContext32, gprs); + + SignalThreadContext32 thread_context; + if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { + LOG(ERROR) << "Couldn't read gprs"; + return false; + } + InitializeCPUContextARM_NoFloatingPoint(thread_context, dest_context); + + dest_context->have_fpa_regs = false; + + LinuxVMAddress reserved_address = + context_address + offsetof(UContext<ContextTraits32>, reserved); + if ((reserved_address & 7) != 0) { + LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address; + return false; + } + + constexpr VMSize kMaxContextSpace = 1024; + + ProcessMemoryRange range; + if (!range.Initialize(memory, false, reserved_address, kMaxContextSpace)) { + return false; + } + + dest_context->have_vfp_regs = false; + do { + CoprocessorContextHead head; + if (!range.Read(reserved_address, sizeof(head), &head)) { + LOG(ERROR) << "missing context terminator"; + return false; + } + reserved_address += sizeof(head); + + switch (head.magic) { + case VFP_MAGIC: + if (head.size != sizeof(SignalVFPContext) + sizeof(head)) { + LOG(ERROR) << "unexpected vfp context size " << head.size; + return false; + } + static_assert( + sizeof(SignalVFPContext::vfp) == sizeof(dest_context->vfp_regs), + "vfp context size mismatch"); + if (!range.Read(reserved_address + offsetof(SignalVFPContext, vfp), + sizeof(dest_context->vfp_regs), + &dest_context->vfp_regs)) { + LOG(ERROR) << "Couldn't read vfp"; + return false; + } + dest_context->have_vfp_regs = true; + return true; + + case CRUNCH_MAGIC: + case IWMMXT_MAGIC: + case DUMMY_MAGIC: + reserved_address += head.size - sizeof(head); + continue; + + case 0: + return true; + + default: + LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic; + return false; + } + } while (true); +} + +template <> +bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>( + ProcessReader* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_union_.arm64; + + CPUContextARM64* dest_context = context_.arm64; + ProcessMemory* memory = reader->Memory(); + + LinuxVMAddress gprs_address = + context_address + offsetof(UContext<ContextTraits64>, mcontext64) + + offsetof(MContext64, gprs); + + ThreadContext::t64_t thread_context; + if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { + LOG(ERROR) << "Couldn't read gprs"; + return false; + } + InitializeCPUContextARM64_NoFloatingPoint(thread_context, dest_context); + + LinuxVMAddress reserved_address = + context_address + offsetof(UContext<ContextTraits64>, reserved); + if ((reserved_address & 15) != 0) { + LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address; + return false; + } + + constexpr VMSize kMaxContextSpace = 4096; + + ProcessMemoryRange range; + if (!range.Initialize(memory, true, reserved_address, kMaxContextSpace)) { + return false; + } + + do { + CoprocessorContextHead head; + if (!range.Read(reserved_address, sizeof(head), &head)) { + LOG(ERROR) << "missing context terminator"; + return false; + } + reserved_address += sizeof(head); + + switch (head.magic) { + case FPSIMD_MAGIC: + if (head.size != sizeof(SignalFPSIMDContext) + sizeof(head)) { + LOG(ERROR) << "unexpected fpsimd context size " << head.size; + return false; + } + SignalFPSIMDContext fpsimd; + if (!range.Read(reserved_address, sizeof(fpsimd), &fpsimd)) { + LOG(ERROR) << "Couldn't read fpsimd " << head.size; + return false; + } + InitializeCPUContextARM64_OnlyFPSIMD(fpsimd, dest_context); + return true; + + case ESR_MAGIC: + case EXTRA_MAGIC: + reserved_address += head.size - sizeof(head); + continue; + + case 0: + LOG(WARNING) << "fpsimd not found"; + InitializeCPUContextARM64_ClearFPSIMD(dest_context); + return true; + + default: + LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic; + return false; + } + } while (true); +} + #endif // ARCH_CPU_X86_FAMILY bool ExceptionSnapshotLinux::Initialize(ProcessReader* process_reader, diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.h index a744356d2a5..73949668e7d 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.h @@ -73,12 +73,15 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot { template <typename Traits> bool ReadContext(ProcessReader* reader, LinuxVMAddress context_address); -#if defined(ARCH_CPU_X86_FAMILY) union { +#if defined(ARCH_CPU_X86_FAMILY) CPUContextX86 x86; CPUContextX86_64 x86_64; - } context_union_; +#elif defined(ARCH_CPU_ARM_FAMILY) + CPUContextARM arm; + CPUContextARM64 arm64; #endif + } context_union_; CPUContext context_; std::vector<uint64_t> codes_; uint64_t thread_id_; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc index bbccf7d80f9..24f0ef52d54 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc @@ -26,6 +26,7 @@ #include "gtest/gtest.h" #include "snapshot/cpu_architecture.h" #include "snapshot/linux/process_reader.h" +#include "snapshot/linux/signal_context.h" #include "sys/syscall.h" #include "test/errors.h" #include "test/linux/fake_ptrace_connection.h" @@ -92,6 +93,176 @@ void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { reinterpret_cast<const char*>(&expected.__fpregs_mem)[byte_offset]); } } +#elif defined(ARCH_CPU_ARMEL) +// A native ucontext_t on ARM doesn't have enough regspace (yet) to hold all of +// the different possible coprocessor contexts at once. However, the ABI allows +// it and the native regspace may be expanded in the future. Append some extra +// space so this is testable now. +struct NativeCPUContext { + ucontext_t ucontext; + char extra[1024]; +}; + +struct CrunchContext { + uint32_t mvdx[16][2]; + uint32_t mvax[4][3]; + uint32_t dspsc[2]; +}; + +struct IWMMXTContext { + uint32_t save[38]; +}; + +struct TestCoprocessorContext { + struct { + internal::CoprocessorContextHead head; + CrunchContext context; + } crunch; + struct { + internal::CoprocessorContextHead head; + IWMMXTContext context; + } iwmmxt; + struct { + internal::CoprocessorContextHead head; + IWMMXTContext context; + } dummy; + struct { + internal::CoprocessorContextHead head; + internal::SignalVFPContext context; + } vfp; + internal::CoprocessorContextHead terminator; +}; + +void InitializeContext(NativeCPUContext* context) { + memset(context, 'x', sizeof(*context)); + + for (int index = 0; index < (&context->ucontext.uc_mcontext.fault_address - + &context->ucontext.uc_mcontext.arm_r0); + ++index) { + (&context->ucontext.uc_mcontext.arm_r0)[index] = index; + } + + static_assert( + sizeof(TestCoprocessorContext) <= + sizeof(context->ucontext.uc_regspace) + sizeof(context->extra), + "Insufficient context space"); + auto test_context = + reinterpret_cast<TestCoprocessorContext*>(context->ucontext.uc_regspace); + + test_context->crunch.head.magic = CRUNCH_MAGIC; + test_context->crunch.head.size = sizeof(test_context->crunch); + memset( + &test_context->crunch.context, 'c', sizeof(test_context->crunch.context)); + + test_context->iwmmxt.head.magic = IWMMXT_MAGIC; + test_context->iwmmxt.head.size = sizeof(test_context->iwmmxt); + memset( + &test_context->iwmmxt.context, 'i', sizeof(test_context->iwmmxt.context)); + + test_context->dummy.head.magic = DUMMY_MAGIC; + test_context->dummy.head.size = sizeof(test_context->dummy); + memset( + &test_context->dummy.context, 'd', sizeof(test_context->dummy.context)); + + test_context->vfp.head.magic = VFP_MAGIC; + test_context->vfp.head.size = sizeof(test_context->vfp); + memset(&test_context->vfp.context, 'v', sizeof(test_context->vfp.context)); + for (size_t reg = 0; reg < arraysize(test_context->vfp.context.vfp.fpregs); + ++reg) { + test_context->vfp.context.vfp.fpregs[reg] = reg; + } + test_context->vfp.context.vfp.fpscr = 42; + + test_context->terminator.magic = 0; + test_context->terminator.size = 0; +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { + EXPECT_EQ(actual.architecture, kCPUArchitectureARM); + + EXPECT_EQ(memcmp(actual.arm->regs, + &expected.ucontext.uc_mcontext.arm_r0, + sizeof(actual.arm->regs)), + 0); + EXPECT_EQ(actual.arm->fp, expected.ucontext.uc_mcontext.arm_fp); + EXPECT_EQ(actual.arm->ip, expected.ucontext.uc_mcontext.arm_ip); + EXPECT_EQ(actual.arm->sp, expected.ucontext.uc_mcontext.arm_sp); + EXPECT_EQ(actual.arm->lr, expected.ucontext.uc_mcontext.arm_lr); + EXPECT_EQ(actual.arm->pc, expected.ucontext.uc_mcontext.arm_pc); + EXPECT_EQ(actual.arm->cpsr, expected.ucontext.uc_mcontext.arm_cpsr); + + EXPECT_FALSE(actual.arm->have_fpa_regs); + + EXPECT_TRUE(actual.arm->have_vfp_regs); + + auto test_context = reinterpret_cast<const TestCoprocessorContext*>( + expected.ucontext.uc_regspace); + + EXPECT_EQ(memcmp(actual.arm->vfp_regs.vfp, + &test_context->vfp.context.vfp, + sizeof(actual.arm->vfp_regs.vfp)), + 0); +} +#elif defined(ARCH_CPU_ARM64) +using NativeCPUContext = ucontext_t; + +struct TestCoprocessorContext { + esr_context esr; + fpsimd_context fpsimd; + _aarch64_ctx terminator; +}; + +void InitializeContext(NativeCPUContext* context) { + memset(context, 'x', sizeof(*context)); + + for (size_t index = 0; index < arraysize(context->uc_mcontext.regs); + ++index) { + context->uc_mcontext.regs[index] = index; + } + context->uc_mcontext.sp = 1; + context->uc_mcontext.pc = 2; + context->uc_mcontext.pstate = 3; + + auto test_context = reinterpret_cast<TestCoprocessorContext*>( + context->uc_mcontext.__reserved); + + test_context->esr.head.magic = ESR_MAGIC; + test_context->esr.head.size = sizeof(test_context->esr); + memset(&test_context->esr.esr, 'e', sizeof(test_context->esr.esr)); + + test_context->fpsimd.head.magic = FPSIMD_MAGIC; + test_context->fpsimd.head.size = sizeof(test_context->fpsimd); + test_context->fpsimd.fpsr = 1; + test_context->fpsimd.fpcr = 2; + for (size_t reg = 0; reg < arraysize(test_context->fpsimd.vregs); ++reg) { + test_context->fpsimd.vregs[reg] = reg; + } + + test_context->terminator.magic = 0; + test_context->terminator.size = 0; +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { + EXPECT_EQ(actual.architecture, kCPUArchitectureARM64); + + EXPECT_EQ(memcmp(actual.arm64->regs, + expected.uc_mcontext.regs, + sizeof(actual.arm64->regs)), + 0); + EXPECT_EQ(actual.arm64->sp, expected.uc_mcontext.sp); + EXPECT_EQ(actual.arm64->pc, expected.uc_mcontext.pc); + EXPECT_EQ(actual.arm64->pstate, expected.uc_mcontext.pstate); + + auto test_context = reinterpret_cast<const TestCoprocessorContext*>( + expected.uc_mcontext.__reserved); + + EXPECT_EQ(actual.arm64->fpsr, test_context->fpsimd.fpsr); + EXPECT_EQ(actual.arm64->fpcr, test_context->fpsimd.fpcr); + EXPECT_EQ(memcmp(actual.arm64->fpsimd, + &test_context->fpsimd.vregs, + sizeof(actual.arm64->fpsimd)), + 0); +} #else #error Port. #endif diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.cc index f328fefc6c5..dfe9d330356 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.cc @@ -64,5 +64,10 @@ bool MemorySnapshotLinux::Read(Delegate* delegate) const { return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); } +const MemorySnapshot* MemorySnapshotLinux::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + return MergeWithOtherSnapshotImpl(this, other); +} + } // namespace internal } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.h index 3c7c8391215..8b4bcf8d0ff 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.h @@ -53,8 +53,15 @@ class MemorySnapshotLinux final : public MemorySnapshot { uint64_t Address() const override; size_t Size() const override; bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; private: + template <class T> + friend const MemorySnapshot* MergeWithOtherSnapshotImpl( + const T* self, + const MemorySnapshot* other); + ProcessReader* process_reader_; // weak uint64_t address_; size_t size_; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.cc new file mode 100644 index 00000000000..0ddbebfa8f0 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.cc @@ -0,0 +1,188 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/module_snapshot_linux.h" + +#include <algorithm> + +#include "base/files/file_path.h" +#include "snapshot/crashpad_types/image_annotation_reader.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotLinux::ModuleSnapshotLinux() + : ModuleSnapshot(), + name_(), + elf_reader_(nullptr), + crashpad_info_(), + type_(kModuleTypeUnknown), + initialized_() {} + +ModuleSnapshotLinux::~ModuleSnapshotLinux() = default; + +bool ModuleSnapshotLinux::Initialize( + const ProcessReader::Module& process_reader_module) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!process_reader_module.elf_reader) { + LOG(ERROR) << "no elf reader"; + return false; + } + + name_ = process_reader_module.name; + elf_reader_ = process_reader_module.elf_reader; + type_ = process_reader_module.type; + + VMAddress info_address; + VMSize info_size; + if (elf_reader_->GetDynamicSymbol( + "g_crashpad_info", &info_address, &info_size)) { + ProcessMemoryRange range; + if (range.Initialize(*elf_reader_->Memory()) && + range.RestrictRange(info_address, info_size)) { + auto info = std::make_unique<CrashpadInfoReader>(); + if (info->Initialize(&range, info_address)) { + crashpad_info_ = std::move(info); + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ModuleSnapshotLinux::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!crashpad_info_) { + return false; + } + + options->crashpad_handler_behavior = + crashpad_info_->CrashpadHandlerBehavior(); + options->system_crash_reporter_forwarding = + crashpad_info_->SystemCrashReporterForwarding(); + options->gather_indirectly_referenced_memory = + crashpad_info_->GatherIndirectlyReferencedMemory(); + options->indirectly_referenced_memory_cap = + crashpad_info_->IndirectlyReferencedMemoryCap(); + return true; +} + +std::string ModuleSnapshotLinux::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotLinux::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_reader_->Address(); +} + +uint64_t ModuleSnapshotLinux::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_reader_->Size(); +} + +time_t ModuleSnapshotLinux::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return 0; +} + +void ModuleSnapshotLinux::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +void ModuleSnapshotLinux::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +ModuleSnapshot::ModuleType ModuleSnapshotLinux::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return type_; +} + +void ModuleSnapshotLinux::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *age = 0; + + std::unique_ptr<ElfImageReader::NoteReader> notes = + elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64); + std::string desc; + notes->NextNote(nullptr, nullptr, &desc); + desc.insert(desc.end(), 16 - std::min(desc.size(), size_t{16}), '\0'); + uuid->InitializeFromBytes(reinterpret_cast<const uint8_t*>(&desc[0])); +} + +std::string ModuleSnapshotLinux::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::FilePath(Name()).BaseName().value(); +} + +std::vector<std::string> ModuleSnapshotLinux::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector<std::string>(); +} + +std::map<std::string, std::string> ModuleSnapshotLinux::AnnotationsSimpleMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::map<std::string, std::string> annotations; + if (crashpad_info_ && crashpad_info_->SimpleAnnotations()) { + ImageAnnotationReader reader(elf_reader_->Memory()); + reader.SimpleMap(crashpad_info_->SimpleAnnotations(), &annotations); + } + return annotations; +} + +std::vector<AnnotationSnapshot> ModuleSnapshotLinux::AnnotationObjects() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector<AnnotationSnapshot> annotations; + if (crashpad_info_ && crashpad_info_->AnnotationsList()) { + ImageAnnotationReader reader(elf_reader_->Memory()); + reader.AnnotationsList(crashpad_info_->AnnotationsList(), &annotations); + } + return annotations; +} + +std::set<CheckedRange<uint64_t>> ModuleSnapshotLinux::ExtraMemoryRanges() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::set<CheckedRange<uint64_t>>(); +} + +std::vector<const UserMinidumpStream*> +ModuleSnapshotLinux::CustomMinidumpStreams() const { + return std::vector<const UserMinidumpStream*>(); +} + +} // namespace internal +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.h new file mode 100644 index 00000000000..b277ff7a66d --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.h @@ -0,0 +1,96 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ + +#include <stdint.h> +#include <sys/types.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/crashpad_types/crashpad_info_reader.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/linux/process_reader.h" +#include "snapshot/module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on a Linux system. +class ModuleSnapshotLinux final : public ModuleSnapshot { + public: + ModuleSnapshotLinux(); + ~ModuleSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader_module The module within the ProcessReader for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(const ProcessReader::Module& process_reader_module); + + //! \brief Returns options from the module’s CrashpadInfo structure. + //! + //! \param[out] options Options set in the module’s CrashpadInfo structure. + //! \return `true` if there were options returned. Otherwise `false`. + bool GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector<std::string> AnnotationsVector() const override; + std::map<std::string, std::string> AnnotationsSimpleMap() const override; + std::vector<AnnotationSnapshot> AnnotationObjects() const override; + std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override; + std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override; + + private: + std::string name_; + ElfImageReader* elf_reader_; + std::unique_ptr<CrashpadInfoReader> crashpad_info_; + ModuleType type_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotLinux); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_ diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc index c9a5a382521..0f196c63ad1 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc @@ -14,6 +14,7 @@ #include "snapshot/linux/process_reader.h" +#include <elf.h> #include <errno.h> #include <sched.h> #include <stdio.h> @@ -26,7 +27,9 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "build/build_config.h" +#include "snapshot/linux/debug_rendezvous.h" #include "util/file/directory_reader.h" +#include "util/linux/auxiliary_vector.h" #include "util/linux/proc_stat_reader.h" #include "util/misc/as_underlying_type.h" @@ -166,14 +169,22 @@ void ProcessReader::Thread::InitializeStack(ProcessReader* reader) { } } +ProcessReader::Module::Module() + : name(), elf_reader(nullptr), type(ModuleSnapshot::kModuleTypeUnknown) {} + +ProcessReader::Module::~Module() = default; + ProcessReader::ProcessReader() : connection_(), process_info_(), memory_map_(), threads_(), + modules_(), + elf_readers_(), process_memory_(), is_64_bit_(false), initialized_threads_(false), + initialized_modules_(false), initialized_() {} ProcessReader::~ProcessReader() {} @@ -250,6 +261,14 @@ const std::vector<ProcessReader::Thread>& ProcessReader::Threads() { return threads_; } +const std::vector<ProcessReader::Module>& ProcessReader::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!initialized_modules_) { + InitializeModules(); + } + return modules_; +} + void ProcessReader::InitializeThreads() { DCHECK(threads_.empty()); @@ -307,4 +326,78 @@ void ProcessReader::InitializeThreads() { DCHECK(main_thread_found); } +void ProcessReader::InitializeModules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + AuxiliaryVector aux; + if (!aux.Initialize(ProcessID(), is_64_bit_)) { + return; + } + + LinuxVMAddress phdrs; + if (!aux.GetValue(AT_PHDR, &phdrs)) { + return; + } + + const MemoryMap::Mapping* exe_mapping; + if (!(exe_mapping = GetMemoryMap()->FindMapping(phdrs)) || + !(exe_mapping = GetMemoryMap()->FindFileMmapStart(*exe_mapping))) { + return; + } + + ProcessMemoryRange range; + if (!range.Initialize(Memory(), is_64_bit_)) { + return; + } + + auto exe_reader = std::make_unique<ElfImageReader>(); + if (!exe_reader->Initialize(range, exe_mapping->range.Base())) { + return; + } + + LinuxVMAddress debug_address; + if (!exe_reader->GetDebugAddress(&debug_address)) { + return; + } + + DebugRendezvous debug; + if (!debug.Initialize(range, debug_address)) { + return; + } + + Module exe = {}; + exe.name = !debug.Executable()->name.empty() ? debug.Executable()->name + : exe_mapping->name; + exe.elf_reader = exe_reader.get(); + exe.type = ModuleSnapshot::ModuleType::kModuleTypeExecutable; + + modules_.push_back(exe); + elf_readers_.push_back(std::move(exe_reader)); + + LinuxVMAddress loader_base = 0; + aux.GetValue(AT_BASE, &loader_base); + + for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) { + const MemoryMap::Mapping* mapping; + if (!(mapping = memory_map_.FindMapping(entry.dynamic_array)) || + !(mapping = memory_map_.FindFileMmapStart(*mapping))) { + continue; + } + + auto elf_reader = std::make_unique<ElfImageReader>(); + if (!elf_reader->Initialize(range, mapping->range.Base())) { + continue; + } + + Module module = {}; + module.name = !entry.name.empty() ? entry.name : mapping->name; + module.elf_reader = elf_reader.get(); + module.type = loader_base && elf_reader->Address() == loader_base + ? ModuleSnapshot::kModuleTypeDynamicLoader + : ModuleSnapshot::kModuleTypeSharedLibrary; + modules_.push_back(module); + elf_readers_.push_back(std::move(elf_reader)); + } +} + } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.h index 26d777e46f0..59a3c7efa98 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.h @@ -18,9 +18,13 @@ #include <sys/time.h> #include <sys/types.h> +#include <memory> +#include <string> #include <vector> #include "base/macros.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/module_snapshot.h" #include "util/linux/address_types.h" #include "util/linux/memory_map.h" #include "util/linux/ptrace_connection.h" @@ -56,6 +60,27 @@ class ProcessReader { void InitializeStack(ProcessReader* reader); }; + //! \brief Contains information about a module loaded into a process. + struct Module { + Module(); + ~Module(); + + //! \brief The pathname used to load the module from disk. + std::string name; + + //! \brief An image reader for the module. + //! + //! The lifetime of this ElfImageReader is scoped to the lifetime of the + //! ProcessReader that created it. + //! + //! This field may be `nullptr` if a reader could not be created for the + //! module. + ElfImageReader* elf_reader; + + //! \brief The module's type. + ModuleSnapshot::ModuleType type; + }; + ProcessReader(); ~ProcessReader(); @@ -107,16 +132,24 @@ class ProcessReader { //! index `0`. const std::vector<Thread>& Threads(); + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable. + const std::vector<Module>& Modules(); + private: void InitializeThreads(); + void InitializeModules(); PtraceConnection* connection_; // weak ProcessInfo process_info_; MemoryMap memory_map_; std::vector<Thread> threads_; + std::vector<Module> modules_; + std::vector<std::unique_ptr<ElfImageReader>> elf_readers_; ProcessMemoryLinux process_memory_; bool is_64_bit_; bool initialized_threads_; + bool initialized_modules_; InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessReader); diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc index 4bdee743653..a8190b39969 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc @@ -15,6 +15,7 @@ #include "snapshot/linux/process_reader.h" #include <errno.h> +#include <link.h> #include <pthread.h> #include <sched.h> #include <stdlib.h> @@ -42,6 +43,10 @@ #include "util/misc/from_pointer_cast.h" #include "util/synchronization/semaphore.h" +#if defined(OS_ANDROID) +#include <android/api-level.h> +#endif + namespace crashpad { namespace test { namespace { @@ -440,6 +445,95 @@ TEST(ProcessReader, ChildWithSplitStack) { test.Run(); } +// Android doesn't provide dl_iterate_phdr on ARM until API 21. +#if !defined(OS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21 +int ExpectFindModule(dl_phdr_info* info, size_t size, void* data) { + SCOPED_TRACE( + base::StringPrintf("module %s at 0x%" PRIx64 " phdrs 0x%" PRIx64, + info->dlpi_name, + LinuxVMAddress{info->dlpi_addr}, + FromPointerCast<LinuxVMAddress>(info->dlpi_phdr))); + auto modules = + reinterpret_cast<const std::vector<ProcessReader::Module>*>(data); + + auto phdr_addr = FromPointerCast<LinuxVMAddress>(info->dlpi_phdr); + +#if defined(OS_ANDROID) + // Bionic includes a null entry. + if (!phdr_addr) { + EXPECT_EQ(info->dlpi_name, nullptr); + EXPECT_EQ(info->dlpi_addr, 0u); + EXPECT_EQ(info->dlpi_phnum, 0u); + return 0; + } +#endif + + // TODO(jperaza): This can use a range map when one is available. + bool found = false; + for (const auto& module : *modules) { + if (module.elf_reader && phdr_addr >= module.elf_reader->Address() && + phdr_addr < module.elf_reader->Address() + module.elf_reader->Size()) { + found = true; + break; + } + } + EXPECT_TRUE(found); + return 0; +} +#endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 + +void ExpectModulesFromSelf(const std::vector<ProcessReader::Module>& modules) { + for (const auto& module : modules) { + EXPECT_FALSE(module.name.empty()); + EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown); + } + +// Android doesn't provide dl_iterate_phdr on ARM until API 21. +#if !defined(OS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21 + EXPECT_EQ(dl_iterate_phdr( + ExpectFindModule, + reinterpret_cast<void*>( + const_cast<std::vector<ProcessReader::Module>*>(&modules))), + 0); +#endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 +} + +TEST(ProcessReader, SelfModules) { + FakePtraceConnection connection; + connection.Initialize(getpid()); + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + ExpectModulesFromSelf(process_reader.Modules()); +} + +class ChildModuleTest : public Multiprocess { + public: + ChildModuleTest() : Multiprocess() {} + ~ChildModuleTest() = default; + + private: + void MultiprocessParent() override { + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); + + ProcessReader process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + ExpectModulesFromSelf(process_reader.Modules()); + } + + void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } + + DISALLOW_COPY_AND_ASSIGN(ChildModuleTest); +}; + +TEST(ProcessReader, ChildModules) { + ChildModuleTest test; + test.Run(); +} + } // namespace } // namespace test } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.cc index 346da0859a9..8397ad9ba44 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.cc @@ -49,6 +49,7 @@ bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { system_.Initialize(&process_reader_, &snapshot_time_); InitializeThreads(); + InitializeModules(); INITIALIZATION_STATE_SET_VALID(initialized_); return true; @@ -78,6 +79,45 @@ bool ProcessSnapshotLinux::InitializeException( return true; } +void ProcessSnapshotLinux::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + CrashpadInfoClientOptions local_options; + + for (const auto& module : modules_) { + CrashpadInfoClientOptions module_options; + if (!module->GetCrashpadOptions(&module_options)) { + continue; + } + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + local_options.indirectly_referenced_memory_cap = + module_options.indirectly_referenced_memory_cap; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset && + local_options.gather_indirectly_referenced_memory != TriState::kUnset) { + break; + } + } + + *options = local_options; +} + pid_t ProcessSnapshotLinux::ProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return process_reader_.ProcessID(); @@ -136,9 +176,11 @@ std::vector<const ThreadSnapshot*> ProcessSnapshotLinux::Threads() const { std::vector<const ModuleSnapshot*> ProcessSnapshotLinux::Modules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); - // TODO(jperaza): do this. - LOG(ERROR) << "Not implemented"; - return std::vector<const ModuleSnapshot*>(); + std::vector<const ModuleSnapshot*> modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; } std::vector<UnloadedModuleSnapshot> ProcessSnapshotLinux::UnloadedModules() @@ -182,4 +224,13 @@ void ProcessSnapshotLinux::InitializeThreads() { } } +void ProcessSnapshotLinux::InitializeModules() { + for (const ProcessReader::Module& reader_module : process_reader_.Modules()) { + auto module = std::make_unique<internal::ModuleSnapshotLinux>(); + if (module->Initialize(reader_module)) { + modules_.push_back(std::move(module)); + } + } +} + } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.h index 446ddb54492..aa6964c453a 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.h @@ -24,7 +24,9 @@ #include <vector> #include "base/macros.h" +#include "snapshot/crashpad_info_client_options.h" #include "snapshot/linux/exception_snapshot_linux.h" +#include "snapshot/linux/module_snapshot_linux.h" #include "snapshot/linux/process_reader.h" #include "snapshot/linux/system_snapshot_linux.h" #include "snapshot/linux/thread_snapshot_linux.h" @@ -86,6 +88,13 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { annotations_simple_map_ = annotations_simple_map; } + //! \brief Returns options from CrashpadInfo structures found in modules in + //! the process. + //! + //! \param[out] options Options set in CrashpadInfo structures in modules in + //! the process. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + // ProcessSnapshot: pid_t ProcessID() const override; @@ -108,12 +117,14 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { private: void InitializeThreads(); + void InitializeModules(); std::map<std::string, std::string> annotations_simple_map_; timeval snapshot_time_; UUID report_id_; UUID client_id_; std::vector<std::unique_ptr<internal::ThreadSnapshotLinux>> threads_; + std::vector<std::unique_ptr<internal::ModuleSnapshotLinux>> modules_; std::unique_ptr<internal::ExceptionSnapshotLinux> exception_; internal::SystemSnapshotLinux system_; ProcessReader process_reader_; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/signal_context.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/signal_context.h index d8d5f69dce0..0476fdfd4ba 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/signal_context.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/signal_context.h @@ -12,13 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_ -#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_ +#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_ +#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_ +#include <signal.h> #include <stdint.h> #include <sys/types.h> +#include <type_traits> + #include "build/build_config.h" +#include "util/linux/thread_info.h" #include "util/linux/traits.h" namespace crashpad { @@ -85,6 +89,35 @@ struct Siginfo { }; }; +template <typename Traits> +struct SignalStack { + typename Traits::Address stack_pointer; + uint32_t flags; + typename Traits::UInteger32_64Only padding; + typename Traits::Size size; +}; + +template <typename Traits, typename Enable = void> +struct Sigset {}; + +template <typename Traits> +struct Sigset< + Traits, + typename std::enable_if<std::is_base_of<Traits32, Traits>::value>::type> { + uint64_t val; +}; + +template <typename Traits> +struct Sigset< + Traits, + typename std::enable_if<std::is_base_of<Traits64, Traits>::value>::type> { +#if defined(OS_ANDROID) + uint64_t val; +#else + typename Traits::ULong val[16]; +#endif // OS_ANDROID +}; + #if defined(ARCH_CPU_X86_FAMILY) struct SignalThreadContext32 { @@ -167,43 +200,105 @@ struct MContext { }; template <typename Traits> -struct SignalStack { - typename Traits::Address stack_pointer; - uint32_t flags; - typename Traits::UInteger32_64Only padding; - typename Traits::Size size; +struct UContext { + typename Traits::ULong flags; + typename Traits::Address link; + SignalStack<Traits> stack; + MContext<Traits> mcontext; + Sigset<Traits> sigmask; + typename Traits::FloatContext fprs; }; -template <typename Traits> -struct Sigset {}; +#elif defined(ARCH_CPU_ARM_FAMILY) -template <> -struct Sigset<ContextTraits32> { - uint64_t val; +struct CoprocessorContextHead { + uint32_t magic; + uint32_t size; }; -#if defined(OS_ANDROID) -template <> -struct Sigset<ContextTraits64> { - uint64_t val; +struct SignalFPSIMDContext { + uint32_t fpsr; + uint32_t fpcr; + uint128_struct vregs[32]; }; -#else -template <> -struct Sigset<ContextTraits64> { - ContextTraits64::ULong val[16]; + +struct SignalVFPContext { + FloatContext::f32_t::vfp_t vfp; + struct vfp_exc { + uint32_t fpexc; + uint32_t fpinst; + uint32_t fpinst2; + } vfp_exc; + uint32_t padding; +}; + +struct SignalThreadContext32 { + uint32_t regs[11]; + uint32_t fp; + uint32_t ip; + uint32_t sp; + uint32_t lr; + uint32_t pc; + uint32_t cpsr; +}; + +using SignalThreadContext64 = ThreadContext::t64_t; + +struct MContext32 { + uint32_t trap_no; + uint32_t error_code; + uint32_t oldmask; + SignalThreadContext32 gprs; + uint32_t fault_address; +}; + +struct MContext64 { + uint64_t fault_address; + SignalThreadContext64 gprs; +}; + +struct ContextTraits32 : public Traits32 { + using MContext32 = MContext32; + using MContext64 = Nothing; +}; + +struct ContextTraits64 : public Traits64 { + using MContext32 = Nothing; + using MContext64 = MContext64; }; -#endif // OS_ANDROID template <typename Traits> struct UContext { typename Traits::ULong flags; typename Traits::Address link; SignalStack<Traits> stack; - MContext<Traits> mcontext; + typename Traits::MContext32 mcontext32; Sigset<Traits> sigmask; - typename Traits::FloatContext fprs; + char padding[128 - sizeof(sigmask)]; + typename Traits::Char_64Only padding2[8]; + typename Traits::MContext64 mcontext64; + typename Traits::Char_64Only padding3[8]; + char reserved[0]; }; +#if defined(ARCH_CPU_ARMEL) +static_assert(offsetof(UContext<ContextTraits32>, mcontext32) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismatch"); +static_assert(offsetof(UContext<ContextTraits32>, reserved) == + offsetof(ucontext_t, uc_regspace), + "regspace offset mismatch"); + +#elif defined(ARCH_CPU_ARM64) +static_assert(offsetof(UContext<ContextTraits64>, mcontext64) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismtach"); +static_assert(offsetof(UContext<ContextTraits64>, reserved) == + offsetof(ucontext_t, uc_mcontext) + + offsetof(mcontext_t, __reserved), + "reserved space offset mismtach"); +#endif + #else #error Port. #endif // ARCH_CPU_X86_FAMILY @@ -213,4 +308,4 @@ struct UContext { } // namespace internal } // namespace crashpad -#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_ +#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_ diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux.cc index f3ed99beb58..c9c643834b7 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux.cc @@ -197,6 +197,9 @@ CPUArchitecture SystemSnapshotLinux::GetCPUArchitecture() const { #if defined(ARCH_CPU_X86_FAMILY) return process_reader_->Is64Bit() ? kCPUArchitectureX86_64 : kCPUArchitectureX86; +#elif defined(ARCH_CPU_ARM_FAMILY) + return process_reader_->Is64Bit() ? kCPUArchitectureARM64 + : kCPUArchitectureARM; #else #error port to your architecture #endif @@ -206,6 +209,9 @@ uint32_t SystemSnapshotLinux::CPURevision() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); #if defined(ARCH_CPU_X86_FAMILY) return cpuid_.Revision(); +#elif defined(ARCH_CPU_ARM_FAMILY) + // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 + return 0; #else #error port to your architecture #endif @@ -220,6 +226,9 @@ std::string SystemSnapshotLinux::CPUVendor() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); #if defined(ARCH_CPU_X86_FAMILY) return cpuid_.Vendor(); +#elif defined(ARCH_CPU_ARM_FAMILY) + // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 + return std::string(); #else #error port to your architecture #endif @@ -264,7 +273,12 @@ uint64_t SystemSnapshotLinux::CPUX86Features() const { uint64_t SystemSnapshotLinux::CPUX86ExtendedFeatures() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) return cpuid_.ExtendedFeatures(); +#else + NOTREACHED(); + return 0; +#endif } uint32_t SystemSnapshotLinux::CPUX86Leaf7Features() const { @@ -340,7 +354,14 @@ std::string SystemSnapshotLinux::MachineDescription() const { bool SystemSnapshotLinux::NXEnabled() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) return cpuid_.NXEnabled(); +#elif defined(ARCH_CPU_ARM_FAMILY) + // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 + return false; +#else +#error Port. +#endif // ARCH_CPU_X86_FAMILY } void SystemSnapshotLinux::TimeZone(DaylightSavingTimeStatus* dst_status, diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc index dcfe5c40dd8..f465a59c331 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc @@ -56,6 +56,20 @@ bool ThreadSnapshotLinux::Initialize( thread.thread_info.float_context.f32, context_.x86); } +#elif defined(ARCH_CPU_ARM_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_union_.arm64; + InitializeCPUContextARM64(thread.thread_info.thread_context.t64, + thread.thread_info.float_context.f64, + context_.arm64); + } else { + context_.architecture = kCPUArchitectureARM; + context_.arm = &context_union_.arm; + InitializeCPUContextARM(thread.thread_info.thread_context.t32, + thread.thread_info.float_context.f32, + context_.arm); + } #else #error Port. #endif diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h index e0ce7adb5e2..1ba291dc464 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h @@ -58,14 +58,17 @@ class ThreadSnapshotLinux final : public ThreadSnapshot { std::vector<const MemorySnapshot*> ExtraMemory() const override; private: -#if defined(ARCH_CPU_X86_FAMILY) union { +#if defined(ARCH_CPU_X86_FAMILY) CPUContextX86 x86; CPUContextX86_64 x86_64; - } context_union_; +#elif defined(ARCH_CPU_ARM_FAMILY) + CPUContextARM arm; + CPUContextARM64 arm64; #else #error Port. #endif // ARCH_CPU_X86_FAMILY + } context_union_; CPUContext context_; MemorySnapshotLinux stack_; LinuxVMAddress thread_specific_data_address_; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc index adc77520c24..ec1d460831a 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc @@ -66,5 +66,10 @@ bool MemorySnapshotMac::Read(Delegate* delegate) const { return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); } +const MemorySnapshot* MemorySnapshotMac::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + return MergeWithOtherSnapshotImpl(this, other); +} + } // namespace internal } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h index bbc39de346d..f06fbf478c8 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h @@ -52,8 +52,15 @@ class MemorySnapshotMac final : public MemorySnapshot { uint64_t Address() const override; size_t Size() const override; bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; private: + template <class T> + friend const MemorySnapshot* MergeWithOtherSnapshotImpl( + const T* self, + const MemorySnapshot* other); + ProcessReader* process_reader_; // weak uint64_t address_; uint64_t size_; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.cc b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.cc new file mode 100644 index 00000000000..a4c8b038c1b --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.cc @@ -0,0 +1,95 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/memory_snapshot.h" + +#include <algorithm> + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { +namespace { + +bool DetermineMergedRangeImpl(bool log, + const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange<uint64_t, size_t>* merged) { + if (a->Size() == 0) { + LOG_IF(ERROR, log) << base::StringPrintf( + "invalid empty range at 0x%" PRIx64, a->Address()); + return false; + } + + if (b->Size() == 0) { + LOG_IF(ERROR, log) << base::StringPrintf( + "invalid empty range at 0x%" PRIx64, b->Address()); + return false; + } + + CheckedRange<uint64_t, size_t> range_a(a->Address(), a->Size()); + if (!range_a.IsValid()) { + LOG_IF(ERROR, log) << base::StringPrintf("invalid range at 0x%" PRIx64 + ", size %" PRIuS, + range_a.base(), + range_a.size()); + return false; + } + + CheckedRange<uint64_t, size_t> range_b(b->Address(), b->Size()); + if (!range_b.IsValid()) { + LOG_IF(ERROR, log) << base::StringPrintf("invalid range at 0x%" PRIx64 + ", size %" PRIuS, + range_b.base(), + range_b.size()); + return false; + } + + if (!range_a.OverlapsRange(range_b) && range_a.end() != range_b.base() && + range_b.end() != range_a.base()) { + LOG_IF(ERROR, log) << base::StringPrintf( + "ranges not overlapping or abutting: (0x%" PRIx64 ", size %" PRIuS + ") and (0x%" PRIx64 ", size %" PRIuS ")", + range_a.base(), + range_a.size(), + range_b.base(), + range_b.size()); + return false; + } + + if (merged) { + uint64_t base = std::min(range_a.base(), range_b.base()); + uint64_t end = std::max(range_a.end(), range_b.end()); + size_t size = static_cast<size_t>(end - base); + merged->SetRange(base, size); + } + return true; +} + +} // namespace + +bool LoggingDetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange<uint64_t, size_t>* merged) { + return DetermineMergedRangeImpl(true, a, b, merged); +} + +bool DetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange<uint64_t, size_t>* merged) { + return DetermineMergedRangeImpl(false, a, b, merged); +} + +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h index 47f8443e617..9e274a0e338 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h @@ -18,6 +18,10 @@ #include <stdint.h> #include <sys/types.h> +#include <memory> + +#include "util/numeric/checked_range.h" + namespace crashpad { //! \brief An abstract interface to a snapshot representing a region of memory @@ -70,8 +74,68 @@ class MemorySnapshot { //! Delegate::MemorySnapshotDelegateRead(), which should be `true` on //! success and `false` on failure. virtual bool Read(Delegate* delegate) const = 0; + + //! \brief Creates a new MemorySnapshot based on merging this one with \a + //! other. + //! + //! The ranges described by the two snapshots must either overlap or abut, and + //! must be of the same concrete type. + //! + //! \return A newly allocated MemorySnapshot representing the merged range, or + //! `nullptr` with an error logged. + virtual const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const = 0; }; +//! \brief Given two memory snapshots, checks if they're overlapping or +//! abutting, and if so, returns the result of merging the two ranges. +//! +//! This function is useful to implement +//! MemorySnapshot::MergeWithOtherSnapshot(). +//! +//! \param[in] a The first range. Must have Size() > 0. +//! \param[in] b The second range. Must have Size() > 0. +//! \param[out] merged The resulting merged range. May be `nullptr` if only a +//! characterization of the ranges is desired. +//! +//! \return `true` if the input ranges overlap or abut, with \a merged filled +//! out, otherwise, `false` with an error logged if \a log is `true`. +bool LoggingDetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange<uint64_t, size_t>* merged); + +//! \brief The same as LoggingDetermineMergedRange but with no errors logged. +//! +//! \sa LoggingDetermineMergedRange +bool DetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange<uint64_t, size_t>* merged); + +namespace internal { + +//! \brief A standard implementation of MemorySnapshot::MergeWithOtherSnapshot() +//! for concrete MemorySnapshot implementations that use a +//! `process_reader_`. +template <class T> +const MemorySnapshot* MergeWithOtherSnapshotImpl(const T* self, + const MemorySnapshot* other) { + const T* other_as_memory_snapshot_concrete = + reinterpret_cast<const T*>(other); + if (self->process_reader_ != + other_as_memory_snapshot_concrete->process_reader_) { + LOG(ERROR) << "different process_reader_ for snapshots"; + return nullptr; + } + CheckedRange<uint64_t, size_t> merged(0, 0); + if (!LoggingDetermineMergedRange(self, other, &merged)) + return nullptr; + + std::unique_ptr<T> result(new T()); + result->Initialize(self->process_reader_, merged.base(), merged.size()); + return result.release(); +} + +} // namespace internal } // namespace crashpad #endif // CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_ diff --git a/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot_test.cc new file mode 100644 index 00000000000..91e847fb26b --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot_test.cc @@ -0,0 +1,152 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/memory_snapshot.h" + +#include "base/macros.h" +#include "gtest/gtest.h" +#include "snapshot/test/test_memory_snapshot.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(DetermineMergedRange, NonOverlapping) { + TestMemorySnapshot a; + TestMemorySnapshot b; + a.SetAddress(0); + a.SetSize(100); + b.SetAddress(200); + b.SetSize(50); + CheckedRange<uint64_t, size_t> range(0, 0); + EXPECT_FALSE(DetermineMergedRange(&a, &b, &range)); + EXPECT_FALSE(DetermineMergedRange(&b, &a, &range)); + + a.SetSize(199); + EXPECT_FALSE(DetermineMergedRange(&a, &b, &range)); +} + +TEST(DetermineMergedRange, Empty) { + TestMemorySnapshot a; + TestMemorySnapshot b; + a.SetAddress(100); + a.SetSize(0); + b.SetAddress(200); + b.SetSize(20); + + CheckedRange<uint64_t, size_t> range(0, 0); + // Empty are invalid. + EXPECT_FALSE(DetermineMergedRange(&a, &b, &range)); + EXPECT_FALSE(DetermineMergedRange(&b, &a, &range)); + EXPECT_FALSE(DetermineMergedRange(&a, &a, &range)); +} + +TEST(DetermineMergedRange, Abutting) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(0); + a.SetSize(10); + b.SetAddress(10); + b.SetSize(20); + + CheckedRange<uint64_t, size_t> range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(0u, range.base()); + EXPECT_EQ(30u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(0u, range.base()); + EXPECT_EQ(30u, range.size()); +} + +TEST(DetermineMergedRange, TypicalOverlapping) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(10); + a.SetSize(100); + b.SetAddress(50); + b.SetSize(100); + + CheckedRange<uint64_t, size_t> range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(10u, range.base()); + EXPECT_EQ(140u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(10u, range.base()); + EXPECT_EQ(140u, range.size()); +} + +TEST(DetermineMergedRange, OneFullyInsideAnother) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(20); + a.SetSize(100); + b.SetAddress(5); + b.SetSize(200); + + CheckedRange<uint64_t, size_t> range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(200u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(200u, range.size()); +} + +TEST(DetermineMergedRange, SameStart) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(5); + a.SetSize(100); + b.SetAddress(5); + b.SetSize(50); + + CheckedRange<uint64_t, size_t> range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); +} + +TEST(DetermineMergedRange, SameEnd) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(5); + a.SetSize(100); + b.SetAddress(70); + b.SetSize(35); + + CheckedRange<uint64_t, size_t> range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp b/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp index d2fc91f5af6..da192341e9c 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp +++ b/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp @@ -39,6 +39,10 @@ 'cpu_context.h', 'crashpad_info_client_options.cc', 'crashpad_info_client_options.h', + 'crashpad_types/crashpad_info_reader.cc', + 'crashpad_types/crashpad_info_reader.h', + 'crashpad_types/image_annotation_reader.cc', + 'crashpad_types/image_annotation_reader.h', 'elf/elf_dynamic_array_reader.cc', 'elf/elf_dynamic_array_reader.h', 'elf/elf_image_reader.cc', @@ -56,6 +60,8 @@ 'linux/exception_snapshot_linux.h', 'linux/memory_snapshot_linux.cc', 'linux/memory_snapshot_linux.h', + 'linux/module_snapshot_linux.cc', + 'linux/module_snapshot_linux.h', 'linux/process_reader.cc', 'linux/process_reader.h', 'linux/process_snapshot_linux.cc', @@ -102,6 +108,7 @@ 'mac/system_snapshot_mac.h', 'mac/thread_snapshot_mac.cc', 'mac/thread_snapshot_mac.h', + 'memory_snapshot.cc', 'memory_snapshot.h', 'minidump/minidump_annotation_reader.cc', 'minidump/minidump_annotation_reader.h', @@ -171,6 +178,7 @@ }, { # else: OS!="linux" and OS!="android" 'sources/': [ ['exclude', '^elf/'], + ['exclude', '^crashpad_types/'], ], }], ['target_arch!="ia32" and target_arch!="x64"', { diff --git a/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp b/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp index 1e32b8e1c29..277bf6e8eb4 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp +++ b/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp @@ -52,6 +52,8 @@ 'target_name': 'crashpad_snapshot_test', 'type': 'executable', 'dependencies': [ + 'crashpad_snapshot_test_both_dt_hash_styles', + 'crashpad_snapshot_test_lib', 'crashpad_snapshot_test_module', 'crashpad_snapshot_test_module_large', 'crashpad_snapshot_test_module_small', @@ -71,7 +73,10 @@ 'sources': [ 'api/module_annotations_win_test.cc', 'cpu_context_test.cc', + 'memory_snapshot_test.cc', 'crashpad_info_client_options_test.cc', + 'crashpad_types/crashpad_info_reader_test.cc', + 'crashpad_types/image_annotation_reader_test.cc', 'elf/elf_image_reader_test.cc', 'elf/elf_image_reader_test_note.S', 'linux/debug_rendezvous_test.cc', @@ -139,6 +144,7 @@ }, { # else: OS!="linux" and OS!="android" 'sources/': [ ['exclude', '^elf/'], + ['exclude', '^crashpad_types/'], ], }], ], @@ -190,6 +196,18 @@ 'crashpad_info_size_test_module.cc', ], }, + { + 'target_name': 'crashpad_snapshot_test_both_dt_hash_styles', + 'type': 'executable', + 'sources': [ + 'hash_types_test.cc', + ], + + 'ldflags': [ + # This makes `ld` emit both .hash and .gnu.hash sections. + '-Wl,--hash-style=both', + ], + }, ], 'conditions': [ ['OS=="mac"', { diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc b/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc index 666cb93693b..17e8ac117a8 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc +++ b/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc @@ -16,7 +16,6 @@ #include "snapshot/win/memory_snapshot_win.h" - namespace crashpad { namespace internal { @@ -67,5 +66,10 @@ bool MemorySnapshotWin::Read(Delegate* delegate) const { return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); } +const MemorySnapshot* MemorySnapshotWin::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + return MergeWithOtherSnapshotImpl(this, other); +} + } // namespace internal } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h b/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h index 04d118426f2..ebc878b81ab 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h @@ -52,8 +52,15 @@ class MemorySnapshotWin final : public MemorySnapshot { uint64_t Address() const override; size_t Size() const override; bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; private: + template <class T> + friend const MemorySnapshot* MergeWithOtherSnapshotImpl( + const T* self, + const MemorySnapshot* other); + ProcessReaderWin* process_reader_; // weak uint64_t address_; size_t size_; diff --git a/chromium/third_party/crashpad/crashpad/snapshot/x86/cpuid_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/x86/cpuid_reader.h index 0fd02cfdb71..b6782afb4f9 100644 --- a/chromium/third_party/crashpad/crashpad/snapshot/x86/cpuid_reader.h +++ b/chromium/third_party/crashpad/crashpad/snapshot/x86/cpuid_reader.h @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifndef CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_ +#define CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_ + #include <stdint.h> #include <string> @@ -61,3 +64,5 @@ class CpuidReader { } // namespace internal } // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_ diff --git a/chromium/third_party/crashpad/crashpad/test/BUILD.gn b/chromium/third_party/crashpad/crashpad/test/BUILD.gn index b8e11d00a21..f7541749624 100644 --- a/chromium/third_party/crashpad/crashpad/test/BUILD.gn +++ b/chromium/third_party/crashpad/crashpad/test/BUILD.gn @@ -32,7 +32,10 @@ static_library("test") { "main_arguments.cc", "main_arguments.h", "multiprocess.h", + "multiprocess_exec.cc", "multiprocess_exec.h", + "process_type.cc", + "process_type.h", "scoped_module_handle.cc", "scoped_module_handle.h", "scoped_temp_dir.cc", @@ -42,13 +45,13 @@ static_library("test") { ] if (crashpad_is_posix) { - sources += [ - "multiprocess_posix.cc", - "scoped_temp_dir_posix.cc", - ] + sources += [ "scoped_temp_dir_posix.cc" ] if (!crashpad_is_fuchsia) { - sources += [ "multiprocess_exec_posix.cc" ] + sources += [ + "multiprocess_exec_posix.cc", + "multiprocess_posix.cc", + ] } } @@ -65,7 +68,7 @@ static_library("test") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/fake_ptrace_connection.cc", "linux/fake_ptrace_connection.h", @@ -91,6 +94,11 @@ static_library("test") { ] } + if (crashpad_is_fuchsia) { + sources += [ "multiprocess_exec_fuchsia.cc" ] + libs = [ "launchpad" ] + } + public_configs = [ "..:crashpad_config" ] configs += [ "../build:crashpad_is_in_chromium" ] @@ -121,6 +129,7 @@ source_set("test_test") { sources = [ "hex_string_test.cc", "main_arguments_test.cc", + "multiprocess_exec_test.cc", "scoped_temp_dir_test.cc", "test_paths_test.cc", ] @@ -141,11 +150,6 @@ source_set("test_test") { } if (!crashpad_is_fuchsia) { - sources += [ - # TODO(scottmg): A MultiprocessExecFuchsia is probably desirable, but - # hasn't been implemented yet. - "multiprocess_exec_test.cc", - ] } deps = [ @@ -166,6 +170,10 @@ executable("crashpad_test_test_multiprocess_exec_test_child") { sources = [ "multiprocess_exec_test_child.cc", ] + + deps = [ + "../third_party/mini_chromium:base", + ] } static_library("gmock_main") { diff --git a/chromium/third_party/crashpad/crashpad/test/test.gyp b/chromium/third_party/crashpad/crashpad/test/test.gyp index ad88e0367a3..a3721efddbd 100644 --- a/chromium/third_party/crashpad/crashpad/test/test.gyp +++ b/chromium/third_party/crashpad/crashpad/test/test.gyp @@ -58,10 +58,13 @@ 'main_arguments.cc', 'main_arguments.h', 'multiprocess.h', + 'multiprocess_exec.cc', 'multiprocess_exec.h', 'multiprocess_exec_posix.cc', 'multiprocess_exec_win.cc', 'multiprocess_posix.cc', + 'process_type.cc', + 'process_type.h', 'scoped_module_handle.cc', 'scoped_module_handle.h', 'scoped_temp_dir.cc', diff --git a/chromium/third_party/crashpad/crashpad/test/test_test.gyp b/chromium/third_party/crashpad/crashpad/test/test_test.gyp index a31650da46a..3c6d7066662 100644 --- a/chromium/third_party/crashpad/crashpad/test/test_test.gyp +++ b/chromium/third_party/crashpad/crashpad/test/test_test.gyp @@ -48,6 +48,9 @@ { 'target_name': 'crashpad_test_test_multiprocess_exec_test_child', 'type': 'executable', + 'dependencies': [ + '../third_party/mini_chromium/mini_chromium.gyp:base', + ], 'sources': [ 'multiprocess_exec_test_child.cc', ], diff --git a/chromium/third_party/crashpad/crashpad/third_party/gtest/BUILD.gn b/chromium/third_party/crashpad/crashpad/third_party/gtest/BUILD.gn index d2702315c9c..1a026119a9a 100644 --- a/chromium/third_party/crashpad/crashpad/third_party/gtest/BUILD.gn +++ b/chromium/third_party/crashpad/crashpad/third_party/gtest/BUILD.gn @@ -32,13 +32,14 @@ if (crashpad_is_in_chromium) { group("gtest") { testonly = true public_deps = [ - "//third_party/gtest", + "//third_party/googletest:gtest", ] } group("gmock") { testonly = true - # TODO(scottmg): Fuchsia doesn't have a third_party/gmock, and has a - # pre-gmock-integration gtest. + public_deps = [ + "//third_party/googletest:gmock", + ] } } else if (crashpad_is_standalone) { config("gtest_private_config") { diff --git a/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/BUILD.gn b/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/BUILD.gn index 9ab8178012d..e11d011181d 100644 --- a/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/BUILD.gn +++ b/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/BUILD.gn @@ -19,13 +19,9 @@ group("base") { public_deps = [ "//base", ] - } else if (crashpad_is_in_fuchsia) { + } else if (crashpad_is_standalone || crashpad_is_in_fuchsia) { public_deps = [ - "//third_party/mini_chromium/base", - ] - } else if (crashpad_is_standalone) { - public_deps = [ - "//third_party/mini_chromium/mini_chromium/base", + "mini_chromium/base", ] } } diff --git a/chromium/third_party/crashpad/crashpad/util/BUILD.gn b/chromium/third_party/crashpad/crashpad/util/BUILD.gn index 9a4970fd862..13a5da80c90 100644 --- a/chromium/third_party/crashpad/crashpad/util/BUILD.gn +++ b/chromium/third_party/crashpad/crashpad/util/BUILD.gn @@ -241,6 +241,10 @@ static_library("util") { } if (crashpad_is_linux) { + sources += [ "net/http_transport_libcurl.cc" ] + } + + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/address_types.h", "linux/auxiliary_vector.cc", @@ -269,10 +273,17 @@ static_library("util") { "linux/thread_info.h", "linux/traits.h", "misc/paths_linux.cc", - "net/http_transport_libcurl.cc", "posix/process_info_linux.cc", "process/process_memory_linux.cc", "process/process_memory_linux.h", + ] + } + + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ + "process/process_memory.cc", + "process/process_memory.h", + "process/process_memory_native.h", # TODO: Port to all platforms. "process/process_memory_range.cc", @@ -361,6 +372,8 @@ static_library("util") { sources += [ "misc/paths_fuchsia.cc", "net/http_transport_fuchsia.cc", + "process/process_memory_fuchsia.cc", + "process/process_memory_fuchsia.h", ] } @@ -450,11 +463,16 @@ source_set("util_test") { "thread/worker_thread_test.cc", ] - if (!crashpad_is_fuchsia) { - # TODO(scottmg): This requires an implementation of MultiprocessExec for - # testing, and a solution to http_transport_test_server.py -- either a port - # to non-Python, or method of forwarding those requests back to the builder + if (!crashpad_is_android && !crashpad_is_fuchsia) { + # Android and Fuchsia will each require an HTTPTransport implementation + # (libcurl isn’t in either’s SDK) and a solution to + # http_transport_test_server.py, because Python isn’t available on either. + # The latter could be ported to non-Python, or the test server could run on + # the build host with a method to forward requests from the device to the # host. + # + # TODO(scottmg): Fuchsia will also require an implementation of + # MultiprocessExec for testing. sources += [ "net/http_transport_test.cc" ] } @@ -493,7 +511,7 @@ source_set("util_test") { ] } - if (crashpad_is_linux) { + if (crashpad_is_linux || crashpad_is_android) { sources += [ "linux/auxiliary_vector_test.cc", "linux/memory_map_test.cc", @@ -501,7 +519,11 @@ source_set("util_test") { "linux/ptrace_broker_test.cc", "linux/ptracer_test.cc", "linux/scoped_ptrace_attach_test.cc", + ] + } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ # TODO: Port to all platforms. "process/process_memory_range_test.cc", "process/process_memory_test.cc", diff --git a/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h b/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h index 4208be67f43..94424dd44d3 100644 --- a/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h +++ b/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h @@ -176,7 +176,7 @@ union FloatContext { } fpregs; // Reflects user_vfp in sys/user.h. - struct vfp { + struct vfp_t { uint64_t fpregs[32]; uint32_t fpscr; } vfp; diff --git a/chromium/third_party/crashpad/crashpad/util/linux/traits.h b/chromium/third_party/crashpad/crashpad/util/linux/traits.h index ca9576814f6..dc5463d8c6e 100644 --- a/chromium/third_party/crashpad/crashpad/util/linux/traits.h +++ b/chromium/third_party/crashpad/crashpad/util/linux/traits.h @@ -26,6 +26,7 @@ struct Traits32 { using ULong = uint32_t; using Clock = Long; using Size = uint32_t; + using Char_64Only = Nothing; using ULong_32Only = ULong; using ULong_64Only = Nothing; using UInteger32_64Only = Nothing; @@ -38,6 +39,7 @@ struct Traits64 { using ULong = uint64_t; using Clock = Long; using Size = uint64_t; + using Char_64Only = char; using ULong_32Only = Nothing; using ULong_64Only = ULong; using UInteger32_64Only = uint32_t; diff --git a/chromium/third_party/crashpad/crashpad/util/misc/address_types.h b/chromium/third_party/crashpad/crashpad/util/misc/address_types.h index bfce35fcfff..870938d35e0 100644 --- a/chromium/third_party/crashpad/crashpad/util/misc/address_types.h +++ b/chromium/third_party/crashpad/crashpad/util/misc/address_types.h @@ -27,6 +27,8 @@ #include "util/win/address_types.h" #elif defined(OS_LINUX) || defined(OS_ANDROID) #include "util/linux/address_types.h" +#elif defined(OS_FUCHSIA) +#include <zircon/types.h> #else #error "Unhandled OS type" #endif @@ -58,6 +60,11 @@ using VMSize = WinVMSize; using VMAddress = LinuxVMAddress; using VMSize = LinuxVMSize; +#elif defined(OS_FUCHSIA) + +using VMAddress = zx_vaddr_t; +using VMSize = size_t; + #endif //! \brief Type used to represent an offset from a VMAddress, potentially diff --git a/chromium/third_party/crashpad/crashpad/util/misc/as_underlying_type.h b/chromium/third_party/crashpad/crashpad/util/misc/as_underlying_type.h index 8afd0ef3462..ba673ae5c29 100644 --- a/chromium/third_party/crashpad/crashpad/util/misc/as_underlying_type.h +++ b/chromium/third_party/crashpad/crashpad/util/misc/as_underlying_type.h @@ -24,7 +24,8 @@ namespace crashpad { //! \param[in] from The value to be casted. //! \return \a from casted to its underlying type. template <typename From> -typename std::underlying_type<From>::type AsUnderlyingType(From from) { +constexpr typename std::underlying_type<From>::type AsUnderlyingType( + From from) { return static_cast<typename std::underlying_type<From>::type>(from); } diff --git a/chromium/third_party/crashpad/crashpad/util/misc/paths_fuchsia.cc b/chromium/third_party/crashpad/crashpad/util/misc/paths_fuchsia.cc index 8baa0d9f1b6..09084483b74 100644 --- a/chromium/third_party/crashpad/crashpad/util/misc/paths_fuchsia.cc +++ b/chromium/third_party/crashpad/crashpad/util/misc/paths_fuchsia.cc @@ -26,9 +26,8 @@ namespace crashpad { bool Paths::Executable(base::FilePath* path) { // Assume the environment has been set up following // https://fuchsia.googlesource.com/docs/+/master/namespaces.md#typical-directory-structure - // . The actual executable name is not known, but it's conceptually in this - // location. - *path = base::FilePath("/pkg/bin/unknown"); + // . + *path = base::FilePath("/pkg/bin/app"); return true; } diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory.cc new file mode 100644 index 00000000000..6bc0010194a --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory.cc @@ -0,0 +1,78 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/process/process_memory.h" + +#include "base/logging.h" + +namespace crashpad { + +bool ProcessMemory::Read(VMAddress address, size_t size, void* buffer) const { + char* buffer_c = static_cast<char*>(buffer); + while (size > 0) { + ssize_t bytes_read = ReadUpTo(address, size, buffer_c); + if (bytes_read < 0) { + return false; + } + if (bytes_read == 0) { + LOG(ERROR) << "short read"; + return false; + } + DCHECK_LE(static_cast<size_t>(bytes_read), size); + size -= bytes_read; + address += bytes_read; + buffer_c += bytes_read; + } + return true; +} + +bool ProcessMemory::ReadCStringInternal(VMAddress address, + bool has_size, + size_t size, + std::string* string) const { + string->clear(); + + char buffer[4096]; + do { + size_t read_size; + if (has_size) { + read_size = std::min(sizeof(buffer), size); + } else { + read_size = sizeof(buffer); + } + + ssize_t bytes_read = ReadUpTo(address, read_size, buffer); + if (bytes_read < 0) { + return false; + } + if (bytes_read == 0) { + break; + } + + char* nul = static_cast<char*>(memchr(buffer, '\0', bytes_read)); + if (nul != nullptr) { + string->append(buffer, nul - buffer); + return true; + } + string->append(buffer, bytes_read); + + address += bytes_read; + size -= bytes_read; + } while (!has_size || size > 0); + + LOG(ERROR) << "unterminated string"; + return false; +} + +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory.h b/chromium/third_party/crashpad/crashpad/util/process/process_memory.h index c046fc6e789..34561718aa5 100644 --- a/chromium/third_party/crashpad/crashpad/util/process/process_memory.h +++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory.h @@ -40,7 +40,7 @@ class ProcessMemory { //! //! \return `true` on success, with \a buffer filled appropriately. `false` on //! failure, with a message logged. - virtual bool Read(VMAddress address, size_t size, void* buffer) const = 0; + bool Read(VMAddress address, size_t size, void* buffer) const; //! \brief Reads a `NUL`-terminated C string from the target process into a //! string in the current process. @@ -83,6 +83,22 @@ class ProcessMemory { ~ProcessMemory() = default; private: + //! \brief Copies memory from the target process into a caller-provided buffer + //! in the current process, up to a maximum number of bytes. + //! + //! \param[in] address The address, in the target process' address space, of + //! the memory region to copy. + //! \param[in] size The maximum size, in bytes, of the memory region to copy. + //! \a buffer must be at least this size. + //! \param[out] buffer The buffer into which the contents of the other + //! process' memory will be copied. + //! + //! \return the number of bytes copied, 0 if there is no more data to read, or + //! -1 on failure with a message logged. + virtual ssize_t ReadUpTo(VMAddress address, + size_t size, + void* buffer) const = 0; + //! \brief Reads a `NUL`-terminated C string from the target process into a //! string in the current process. //! @@ -102,7 +118,7 @@ class ProcessMemory { virtual bool ReadCStringInternal(VMAddress address, bool has_size, size_t size, - std::string* string) const = 0; + std::string* string) const; }; } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.cc new file mode 100644 index 00000000000..212e1c6ffe6 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.cc @@ -0,0 +1,56 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "util/process/process_memory_fuchsia.h" + +#include <zircon/syscalls.h> + +#include <limits> + +#include "base/logging.h" +#include "base/fuchsia/fuchsia_logging.h" + +namespace crashpad { + +ProcessMemoryFuchsia::ProcessMemoryFuchsia() + : ProcessMemory(), process_(), initialized_() {} + +ProcessMemoryFuchsia::~ProcessMemoryFuchsia() {} + +bool ProcessMemoryFuchsia::Initialize(zx_handle_t process) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + process_ = process; + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +ssize_t ProcessMemoryFuchsia::ReadUpTo(VMAddress address, + size_t size, + void* buffer) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()}); + + size_t actual; + zx_status_t status = + zx_process_read_memory(process_, address, buffer, size, &actual); + + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_process_read_memory"; + return -1; + } + + return actual; +} + +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.h b/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.h new file mode 100644 index 00000000000..e6cdd3e8c15 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.h @@ -0,0 +1,56 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_ +#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_ + +#include <zircon/types.h> + +#include <string> + +#include "base/macros.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory.h" + +namespace crashpad { + +//! \brief Accesses the memory of another Fuchsia process. +class ProcessMemoryFuchsia final : public ProcessMemory { + public: + ProcessMemoryFuchsia(); + ~ProcessMemoryFuchsia(); + + //! \brief Initializes this object to read the memory of a process by handle. + //! + //! This method must be called successfully prior to calling any other method + //! in this class. + //! + //! \param[in] process The handle to the target process. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool Initialize(zx_handle_t process); + + private: + ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override; + + zx_handle_t process_; + InitializationStateDcheck initialized_; + + DISALLOW_COPY_AND_ASSIGN(ProcessMemoryFuchsia); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_ diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.cc index f294ee34ff5..1fc3c05f1ce 100644 --- a/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.cc +++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.cc @@ -20,6 +20,7 @@ #include <unistd.h> #include <algorithm> +#include <limits> #include "base/logging.h" #include "base/posix/eintr_wrapper.h" @@ -27,11 +28,12 @@ namespace crashpad { ProcessMemoryLinux::ProcessMemoryLinux() - : ProcessMemory(), mem_fd_(), pid_(-1) {} + : ProcessMemory(), mem_fd_(), pid_(-1), initialized_() {} ProcessMemoryLinux::~ProcessMemoryLinux() {} bool ProcessMemoryLinux::Initialize(pid_t pid) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); pid_ = pid; char path[32]; snprintf(path, sizeof(path), "/proc/%d/mem", pid_); @@ -40,75 +42,23 @@ bool ProcessMemoryLinux::Initialize(pid_t pid) { PLOG(ERROR) << "open"; return false; } + INITIALIZATION_STATE_SET_VALID(initialized_); return true; } -bool ProcessMemoryLinux::Read(VMAddress address, - size_t size, - void* buffer) const { +ssize_t ProcessMemoryLinux::ReadUpTo(VMAddress address, + size_t size, + void* buffer) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); DCHECK(mem_fd_.is_valid()); + DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()}); - char* buffer_c = static_cast<char*>(buffer); - while (size > 0) { - ssize_t bytes_read = - HANDLE_EINTR(pread64(mem_fd_.get(), buffer_c, size, address)); - if (bytes_read < 0) { - PLOG(ERROR) << "pread64"; - return false; - } - if (bytes_read == 0) { - LOG(ERROR) << "unexpected eof"; - return false; - } - DCHECK_LE(static_cast<size_t>(bytes_read), size); - size -= bytes_read; - address += bytes_read; - buffer_c += bytes_read; + ssize_t bytes_read = + HANDLE_EINTR(pread64(mem_fd_.get(), buffer, size, address)); + if (bytes_read < 0) { + PLOG(ERROR) << "pread64"; } - return true; -} - -bool ProcessMemoryLinux::ReadCStringInternal(VMAddress address, - bool has_size, - size_t size, - std::string* string) const { - DCHECK(mem_fd_.is_valid()); - - string->clear(); - - char buffer[4096]; - do { - size_t read_size; - if (has_size) { - read_size = std::min(sizeof(buffer), size); - } else { - read_size = sizeof(buffer); - } - ssize_t bytes_read; - bytes_read = - HANDLE_EINTR(pread64(mem_fd_.get(), buffer, read_size, address)); - if (bytes_read < 0) { - PLOG(ERROR) << "pread64"; - return false; - } - if (bytes_read == 0) { - break; - } - DCHECK_LE(static_cast<size_t>(bytes_read), read_size); - - char* nul = static_cast<char*>(memchr(buffer, '\0', bytes_read)); - if (nul != nullptr) { - string->append(buffer, nul - buffer); - return true; - } - string->append(buffer, bytes_read); - - address += bytes_read; - size -= bytes_read; - } while (!has_size || size > 0); - - LOG(ERROR) << "unterminated string"; - return false; + return bytes_read; } } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.h b/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.h index e03e251f0ff..4fe008bb295 100644 --- a/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.h +++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.h @@ -22,6 +22,7 @@ #include "base/files/scoped_file.h" #include "base/macros.h" #include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" #include "util/process/process_memory.h" namespace crashpad { @@ -43,16 +44,12 @@ class ProcessMemoryLinux final : public ProcessMemory { //! \return `true` on success, `false` on failure with a message logged. bool Initialize(pid_t pid); - bool Read(VMAddress address, size_t size, void* buffer) const override; - private: - bool ReadCStringInternal(VMAddress address, - bool has_size, - size_t size, - std::string* string) const override; + ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override; base::ScopedFD mem_fd_; pid_t pid_; + InitializationStateDcheck initialized_; DISALLOW_COPY_AND_ASSIGN(ProcessMemoryLinux); }; diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_native.h b/chromium/third_party/crashpad/crashpad/util/process/process_memory_native.h new file mode 100644 index 00000000000..19fa805a278 --- /dev/null +++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_native.h @@ -0,0 +1,34 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "build/build_config.h" + +#if defined(OS_FUCHSIA) +#include "util/process/process_memory_fuchsia.h" +#elif defined(OS_LINUX) || defined(OS_ANDROID) +#include "util/process/process_memory_linux.h" +#endif + +namespace crashpad { + +#if defined(OS_FUCHSIA) || DOXYGEN +//! \brief Alias for platform-specific native implementation of ProcessMemory. +using ProcessMemoryNative = ProcessMemoryFuchsia; +#elif defined(OS_LINUX) || defined(OS_ANDROID) +using ProcessMemoryNative = ProcessMemoryLinux; +#else +#error Port. +#endif + +} // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc index c4cab7e0dde..1c785068628 100644 --- a/chromium/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc +++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc @@ -22,7 +22,14 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "util/misc/from_pointer_cast.h" + +#if defined(OS_FUCHSIA) +#include <zircon/process.h> + +#include "util/process/process_memory_fuchsia.h" +#else #include "util/process/process_memory_linux.h" +#endif namespace crashpad { namespace test { @@ -34,6 +41,11 @@ struct TestObject { } kTestObject = {"string1", "string2"}; TEST(ProcessMemoryRange, Basic) { +#if defined(OS_FUCHSIA) + ProcessMemoryFuchsia memory; + ASSERT_TRUE(memory.Initialize(zx_process_self())); + constexpr bool is_64_bit = true; +#else pid_t pid = getpid(); #if defined(ARCH_CPU_64_BITS) constexpr bool is_64_bit = true; @@ -43,6 +55,7 @@ TEST(ProcessMemoryRange, Basic) { ProcessMemoryLinux memory; ASSERT_TRUE(memory.Initialize(pid)); +#endif // OS_FUCHSIA ProcessMemoryRange range; ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_test.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory_test.cc index 5f631237759..5827638f9ee 100644 --- a/chromium/third_party/crashpad/crashpad/util/process/process_memory_test.cc +++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_test.cc @@ -23,92 +23,117 @@ #include "gtest/gtest.h" #include "test/errors.h" #include "test/multiprocess.h" +#include "test/multiprocess_exec.h" +#include "test/process_type.h" #include "util/file/file_io.h" #include "util/misc/from_pointer_cast.h" #include "util/posix/scoped_mmap.h" -#include "util/process/process_memory_linux.h" +#include "util/process/process_memory_native.h" namespace crashpad { namespace test { namespace { -class TargetProcessTest : public Multiprocess { - public: - TargetProcessTest() : Multiprocess() {} - ~TargetProcessTest() {} - - void RunAgainstSelf() { DoTest(getpid()); } - - void RunAgainstForked() { Run(); } +void DoChildReadTestSetup(size_t* region_size, + std::unique_ptr<char[]>* region) { + *region_size = 4 * getpagesize(); + region->reset(new char[*region_size]); + for (size_t index = 0; index < *region_size; ++index) { + (*region)[index] = index % 256; + } +} - private: - void MultiprocessParent() override { DoTest(ChildPID()); } +CRASHPAD_CHILD_TEST_MAIN(ReadTestChild) { + size_t region_size; + std::unique_ptr<char[]> region; + DoChildReadTestSetup(®ion_size, ®ion); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + CheckedWriteFile(out, ®ion_size, sizeof(region_size)); + VMAddress address = FromPointerCast<VMAddress>(region.get()); + CheckedWriteFile(out, &address, sizeof(address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} - void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } +class ReadTest : public MultiprocessExec { + public: + ReadTest() : MultiprocessExec() { + SetChildTestMainFunction("ReadTestChild"); + } - virtual void DoTest(pid_t pid) = 0; + void RunAgainstSelf() { + size_t region_size; + std::unique_ptr<char[]> region; + DoChildReadTestSetup(®ion_size, ®ion); + DoTest(GetSelfProcess(), + region_size, + FromPointerCast<VMAddress>(region.get())); + } - DISALLOW_COPY_AND_ASSIGN(TargetProcessTest); -}; + void RunAgainstChild() { Run(); } -class ReadTest : public TargetProcessTest { - public: - ReadTest() - : TargetProcessTest(), - page_size_(getpagesize()), - region_size_(4 * page_size_), - region_(new char[region_size_]) { - for (size_t index = 0; index < region_size_; ++index) { - region_[index] = index % 256; - } + private: + void MultiprocessParent() override { + size_t region_size; + VMAddress region; + ASSERT_TRUE( + ReadFileExactly(ReadPipeHandle(), ®ion_size, sizeof(region_size))); + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), ®ion, sizeof(region))); + DoTest(ChildProcess(), region_size, region); } - private: - void DoTest(pid_t pid) override { - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); + void DoTest(ProcessType process, size_t region_size, VMAddress address) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); - VMAddress address = FromPointerCast<VMAddress>(region_.get()); - std::unique_ptr<char[]> result(new char[region_size_]); + std::unique_ptr<char[]> result(new char[region_size]); // Ensure that the entire region can be read. - ASSERT_TRUE(memory.Read(address, region_size_, result.get())); - EXPECT_EQ(memcmp(region_.get(), result.get(), region_size_), 0); + ASSERT_TRUE(memory.Read(address, region_size, result.get())); + for (size_t i = 0; i < region_size; ++i) { + EXPECT_EQ(result[i], static_cast<char>(i % 256)); + } // Ensure that a read of length 0 succeeds and doesn’t touch the result. - memset(result.get(), '\0', region_size_); + memset(result.get(), '\0', region_size); ASSERT_TRUE(memory.Read(address, 0, result.get())); - for (size_t i = 0; i < region_size_; ++i) { + for (size_t i = 0; i < region_size; ++i) { EXPECT_EQ(result[i], 0); } // Ensure that a read starting at an unaligned address works. - ASSERT_TRUE(memory.Read(address + 1, region_size_ - 1, result.get())); - EXPECT_EQ(memcmp(region_.get() + 1, result.get(), region_size_ - 1), 0); + ASSERT_TRUE(memory.Read(address + 1, region_size - 1, result.get())); + for (size_t i = 0; i < region_size - 1; ++i) { + EXPECT_EQ(result[i], static_cast<char>((i + 1) % 256)); + } // Ensure that a read ending at an unaligned address works. - ASSERT_TRUE(memory.Read(address, region_size_ - 1, result.get())); - EXPECT_EQ(memcmp(region_.get(), result.get(), region_size_ - 1), 0); + ASSERT_TRUE(memory.Read(address, region_size - 1, result.get())); + for (size_t i = 0; i < region_size - 1; ++i) { + EXPECT_EQ(result[i], static_cast<char>(i % 256)); + } // Ensure that a read starting and ending at unaligned addresses works. - ASSERT_TRUE(memory.Read(address + 1, region_size_ - 2, result.get())); - EXPECT_EQ(memcmp(region_.get() + 1, result.get(), region_size_ - 2), 0); + ASSERT_TRUE(memory.Read(address + 1, region_size - 2, result.get())); + for (size_t i = 0; i < region_size - 2; ++i) { + EXPECT_EQ(result[i], static_cast<char>((i + 1) % 256)); + } // Ensure that a read of exactly one page works. - ASSERT_TRUE(memory.Read(address + page_size_, page_size_, result.get())); - EXPECT_EQ(memcmp(region_.get() + page_size_, result.get(), page_size_), 0); + size_t page_size = getpagesize(); + ASSERT_GE(region_size, page_size + page_size); + ASSERT_TRUE(memory.Read(address + page_size, page_size, result.get())); + for (size_t i = 0; i < page_size; ++i) { + EXPECT_EQ(result[i], static_cast<char>((i + page_size) % 256)); + } // Ensure that reading exactly a single byte works. result[1] = 'J'; ASSERT_TRUE(memory.Read(address + 2, 1, result.get())); - EXPECT_EQ(result[0], region_[2]); + EXPECT_EQ(result[0], 2); EXPECT_EQ(result[1], 'J'); } - const size_t page_size_; - const size_t region_size_; - std::unique_ptr<char[]> region_; - DISALLOW_COPY_AND_ASSIGN(ReadTest); }; @@ -117,96 +142,161 @@ TEST(ProcessMemory, ReadSelf) { test.RunAgainstSelf(); } -TEST(ProcessMemory, ReadForked) { +TEST(ProcessMemory, ReadChild) { ReadTest test; - test.RunAgainstForked(); + test.RunAgainstChild(); } -bool ReadCString(const ProcessMemory& memory, - const char* pointer, - std::string* result) { - return memory.ReadCString(FromPointerCast<VMAddress>(pointer), result); +constexpr char kConstCharEmpty[] = ""; +constexpr char kConstCharShort[] = "A short const char[]"; + +#define SHORT_LOCAL_STRING "A short local variable char[]" + +std::string MakeLongString() { + std::string long_string; + const size_t kStringLongSize = 4 * getpagesize(); + for (size_t index = 0; index < kStringLongSize; ++index) { + long_string.push_back((index % 255) + 1); + } + EXPECT_EQ(long_string.size(), kStringLongSize); + return long_string; } -bool ReadCStringSizeLimited(const ProcessMemory& memory, - const char* pointer, - size_t size, - std::string* result) { - return memory.ReadCStringSizeLimited( - FromPointerCast<VMAddress>(pointer), size, result); +void DoChildCStringReadTestSetup(const char** const_empty, + const char** const_short, + const char** local_empty, + const char** local_short, + std::string* long_string) { + *const_empty = kConstCharEmpty; + *const_short = kConstCharShort; + *local_empty = ""; + *local_short = SHORT_LOCAL_STRING; + *long_string = MakeLongString(); } -constexpr char kConstCharEmpty[] = ""; -constexpr char kConstCharShort[] = "A short const char[]"; +CRASHPAD_CHILD_TEST_MAIN(ReadCStringTestChild) { + const char* const_empty; + const char* const_short; + const char* local_empty; + const char* local_short; + std::string long_string; + DoChildCStringReadTestSetup( + &const_empty, &const_short, &local_empty, &local_short, &long_string); + const auto write_address = [](const char* p) { + VMAddress address = FromPointerCast<VMAddress>(p); + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &address, + sizeof(address)); + }; + write_address(const_empty); + write_address(const_short); + write_address(local_empty); + write_address(local_short); + write_address(long_string.c_str()); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} -class ReadCStringTest : public TargetProcessTest { +class ReadCStringTest : public MultiprocessExec { public: ReadCStringTest(bool limit_size) - : TargetProcessTest(), - member_char_empty_(""), - member_char_short_("A short member char[]"), - limit_size_(limit_size) { - const size_t kStringLongSize = 4 * getpagesize(); - for (size_t index = 0; index < kStringLongSize; ++index) { - string_long_.push_back((index % 255) + 1); - } - EXPECT_EQ(string_long_.size(), kStringLongSize); + : MultiprocessExec(), limit_size_(limit_size) { + SetChildTestMainFunction("ReadCStringTestChild"); } + void RunAgainstSelf() { + const char* const_empty; + const char* const_short; + const char* local_empty; + const char* local_short; + std::string long_string; + DoChildCStringReadTestSetup( + &const_empty, &const_short, &local_empty, &local_short, &long_string); + DoTest(GetSelfProcess(), + FromPointerCast<VMAddress>(const_empty), + FromPointerCast<VMAddress>(const_short), + FromPointerCast<VMAddress>(local_empty), + FromPointerCast<VMAddress>(local_short), + FromPointerCast<VMAddress>(long_string.c_str())); + } + void RunAgainstChild() { Run(); } + private: - void DoTest(pid_t pid) override { - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); + void MultiprocessParent() override { +#define DECLARE_AND_READ_ADDRESS(name) \ + VMAddress name; \ + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &name, sizeof(name))); + DECLARE_AND_READ_ADDRESS(const_empty_address); + DECLARE_AND_READ_ADDRESS(const_short_address); + DECLARE_AND_READ_ADDRESS(local_empty_address); + DECLARE_AND_READ_ADDRESS(local_short_address); + DECLARE_AND_READ_ADDRESS(long_string_address); +#undef DECLARE_AND_READ_ADDRESS + + DoTest(ChildProcess(), + const_empty_address, + const_short_address, + local_empty_address, + local_short_address, + long_string_address); + } + + void DoTest(ProcessType process, + VMAddress const_empty_address, + VMAddress const_short_address, + VMAddress local_empty_address, + VMAddress local_short_address, + VMAddress long_string_address) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); std::string result; if (limit_size_) { - ASSERT_TRUE(ReadCStringSizeLimited( - memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result)); + ASSERT_TRUE(memory.ReadCStringSizeLimited( + const_empty_address, arraysize(kConstCharEmpty), &result)); EXPECT_EQ(result, kConstCharEmpty); - ASSERT_TRUE(ReadCStringSizeLimited( - memory, kConstCharShort, arraysize(kConstCharShort), &result)); + ASSERT_TRUE(memory.ReadCStringSizeLimited( + const_short_address, arraysize(kConstCharShort), &result)); EXPECT_EQ(result, kConstCharShort); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result)); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, member_char_empty_, strlen(member_char_empty_) + 1, &result)); - EXPECT_EQ(result, member_char_empty_); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, member_char_short_, strlen(member_char_short_) + 1, &result)); - EXPECT_EQ(result, member_char_short_); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, member_char_short_, strlen(member_char_short_), &result)); - - ASSERT_TRUE(ReadCStringSizeLimited( - memory, string_long_.c_str(), string_long_.size() + 1, &result)); - EXPECT_EQ(result, string_long_); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, string_long_.c_str(), string_long_.size(), &result)); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + const_short_address, arraysize(kConstCharShort) - 1, &result)); + + ASSERT_TRUE( + memory.ReadCStringSizeLimited(local_empty_address, 1, &result)); + EXPECT_EQ(result, ""); + + ASSERT_TRUE(memory.ReadCStringSizeLimited( + local_short_address, strlen(SHORT_LOCAL_STRING) + 1, &result)); + EXPECT_EQ(result, SHORT_LOCAL_STRING); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + local_short_address, strlen(SHORT_LOCAL_STRING), &result)); + + std::string long_string_for_comparison = MakeLongString(); + ASSERT_TRUE(memory.ReadCStringSizeLimited( + long_string_address, long_string_for_comparison.size() + 1, &result)); + EXPECT_EQ(result, long_string_for_comparison); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + long_string_address, long_string_for_comparison.size(), &result)); } else { - ASSERT_TRUE(ReadCString(memory, kConstCharEmpty, &result)); + ASSERT_TRUE(memory.ReadCString(const_empty_address, &result)); EXPECT_EQ(result, kConstCharEmpty); - ASSERT_TRUE(ReadCString(memory, kConstCharShort, &result)); + ASSERT_TRUE(memory.ReadCString(const_short_address, &result)); EXPECT_EQ(result, kConstCharShort); - ASSERT_TRUE(ReadCString(memory, member_char_empty_, &result)); - EXPECT_EQ(result, member_char_empty_); + ASSERT_TRUE(memory.ReadCString(local_empty_address, &result)); + EXPECT_EQ(result, ""); - ASSERT_TRUE(ReadCString(memory, member_char_short_, &result)); - EXPECT_EQ(result, member_char_short_); + ASSERT_TRUE(memory.ReadCString(local_short_address, &result)); + EXPECT_EQ(result, SHORT_LOCAL_STRING); - ASSERT_TRUE(ReadCString(memory, string_long_.c_str(), &result)); - EXPECT_EQ(result, string_long_); + ASSERT_TRUE(memory.ReadCString(long_string_address, &result)); + EXPECT_EQ(result, MakeLongString()); } } - std::string string_long_; - const char* member_char_empty_; - const char* member_char_short_; const bool limit_size_; DISALLOW_COPY_AND_ASSIGN(ReadCStringTest); @@ -217,9 +307,9 @@ TEST(ProcessMemory, ReadCStringSelf) { test.RunAgainstSelf(); } -TEST(ProcessMemory, ReadCStringForked) { +TEST(ProcessMemory, ReadCStringChild) { ReadCStringTest test(/* limit_size= */ false); - test.RunAgainstForked(); + test.RunAgainstChild(); } TEST(ProcessMemory, ReadCStringSizeLimitedSelf) { @@ -227,56 +317,96 @@ TEST(ProcessMemory, ReadCStringSizeLimitedSelf) { test.RunAgainstSelf(); } -TEST(ProcessMemory, ReadCStringSizeLimitedForked) { +TEST(ProcessMemory, ReadCStringSizeLimitedChild) { ReadCStringTest test(/* limit_size= */ true); - test.RunAgainstForked(); + test.RunAgainstChild(); } -class ReadUnmappedTest : public TargetProcessTest { - public: - ReadUnmappedTest() - : TargetProcessTest(), - page_size_(getpagesize()), - region_size_(2 * page_size_), - result_(new char[region_size_]) { - if (!pages_.ResetMmap(nullptr, - region_size_, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, - 0)) { - ADD_FAILURE(); - return; - } +void DoReadUnmappedChildMainSetup(ScopedMmap* pages, + VMAddress* address, + size_t* page_size, + size_t* region_size) { + *page_size = getpagesize(); + *region_size = 2 * (*page_size); + if (!pages->ResetMmap(nullptr, + *region_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0)) { + ADD_FAILURE(); + return; + } - char* region = pages_.addr_as<char*>(); - for (size_t index = 0; index < region_size_; ++index) { - region[index] = index % 256; - } + *address = pages->addr_as<VMAddress>(); - EXPECT_TRUE(pages_.ResetAddrLen(region, page_size_)); + char* region = pages->addr_as<char*>(); + for (size_t index = 0; index < *region_size; ++index) { + region[index] = index % 256; } - private: - void DoTest(pid_t pid) override { - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); + EXPECT_TRUE(pages->ResetAddrLen(region, *page_size)); +} - VMAddress page_addr1 = pages_.addr_as<VMAddress>(); - VMAddress page_addr2 = page_addr1 + page_size_; +CRASHPAD_CHILD_TEST_MAIN(ReadUnmappedChildMain) { + ScopedMmap pages; + VMAddress address; + size_t page_size, region_size; + DoReadUnmappedChildMainSetup(&pages, &address, &page_size, ®ion_size); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + CheckedWriteFile(out, &address, sizeof(address)); + CheckedWriteFile(out, &page_size, sizeof(page_size)); + CheckedWriteFile(out, ®ion_size, sizeof(region_size)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadUnmappedTest : public MultiprocessExec { + public: + ReadUnmappedTest() : MultiprocessExec() { + SetChildTestMainFunction("ReadUnmappedChildMain"); + } + + void RunAgainstSelf() { + ScopedMmap pages; + VMAddress address; + size_t page_size, region_size; + DoReadUnmappedChildMainSetup(&pages, &address, &page_size, ®ion_size); + DoTest(GetSelfProcess(), address, page_size, region_size); + } - EXPECT_TRUE(memory.Read(page_addr1, page_size_, result_.get())); - EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result_.get())); + void RunAgainstChild() { Run(); } - EXPECT_FALSE(memory.Read(page_addr1, region_size_, result_.get())); - EXPECT_FALSE(memory.Read(page_addr2, page_size_, result_.get())); - EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result_.get())); + private: + void MultiprocessParent() override { + VMAddress address; + size_t page_size, region_size; + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &address, sizeof(address))); + ASSERT_TRUE( + ReadFileExactly(ReadPipeHandle(), &page_size, sizeof(page_size))); + ASSERT_TRUE( + ReadFileExactly(ReadPipeHandle(), ®ion_size, sizeof(region_size))); + DoTest(ChildProcess(), address, page_size, region_size); } - ScopedMmap pages_; - const size_t page_size_; - const size_t region_size_; - std::unique_ptr<char[]> result_; + void DoTest(ProcessType process, + VMAddress address, + size_t page_size, + size_t region_size) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); + + VMAddress page_addr1 = address; + VMAddress page_addr2 = page_addr1 + page_size; + + std::unique_ptr<char[]> result(new char[region_size]); + EXPECT_TRUE(memory.Read(page_addr1, page_size, result.get())); + EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result.get())); + + EXPECT_FALSE(memory.Read(page_addr1, region_size, result.get())); + EXPECT_FALSE(memory.Read(page_addr2, page_size, result.get())); + EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result.get())); + } DISALLOW_COPY_AND_ASSIGN(ReadUnmappedTest); }; @@ -287,90 +417,158 @@ TEST(ProcessMemory, ReadUnmappedSelf) { test.RunAgainstSelf(); } -TEST(ProcessMemory, ReadUnmappedForked) { +TEST(ProcessMemory, ReadUnmappedChild) { ReadUnmappedTest test; ASSERT_FALSE(testing::Test::HasFailure()); - test.RunAgainstForked(); + test.RunAgainstChild(); } -class ReadCStringUnmappedTest : public TargetProcessTest { +constexpr size_t kChildProcessStringLength = 10; + +class StringDataInChildProcess { public: - ReadCStringUnmappedTest(bool limit_size) - : TargetProcessTest(), - page_size_(getpagesize()), - region_size_(2 * page_size_), - limit_size_(limit_size) { - if (!pages_.ResetMmap(nullptr, - region_size_, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, - 0)) { - ADD_FAILURE(); - return; - } + // This constructor only makes sense in the child process. + explicit StringDataInChildProcess(const char* cstring) + : address_(FromPointerCast<VMAddress>(cstring)) { + memcpy(expected_value_, cstring, kChildProcessStringLength + 1); + } - char* region = pages_.addr_as<char*>(); - for (size_t index = 0; index < region_size_; ++index) { - region[index] = 1 + index % 255; - } + void Write(FileHandle out) { + CheckedWriteFile(out, &address_, sizeof(address_)); + CheckedWriteFile(out, &expected_value_, sizeof(expected_value_)); + } + + static StringDataInChildProcess Read(FileHandle in) { + StringDataInChildProcess str; + EXPECT_TRUE(ReadFileExactly(in, &str.address_, sizeof(str.address_))); + EXPECT_TRUE( + ReadFileExactly(in, &str.expected_value_, sizeof(str.expected_value_))); + return str; + } + + VMAddress address() const { return address_; } + std::string expected_value() const { return expected_value_; } - // A string at the start of the mapped region - string1_ = region; - string1_[expected_length_] = '\0'; + private: + StringDataInChildProcess() : address_(0), expected_value_() {} - // A string near the end of the mapped region - string2_ = region + page_size_ - expected_length_ * 2; - string2_[expected_length_] = '\0'; + VMAddress address_; + char expected_value_[kChildProcessStringLength + 1]; +}; + +void DoCStringUnmappedTestSetup( + ScopedMmap* pages, + std::vector<StringDataInChildProcess>* strings) { + const size_t page_size = getpagesize(); + const size_t region_size = 2 * page_size; + if (!pages->ResetMmap(nullptr, + region_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0)) { + ADD_FAILURE(); + return; + } + + char* region = pages->addr_as<char*>(); + for (size_t index = 0; index < region_size; ++index) { + region[index] = 1 + index % 255; + } + + // A string at the start of the mapped region + char* string1 = region; + string1[kChildProcessStringLength] = '\0'; + + // A string near the end of the mapped region + char* string2 = region + page_size - kChildProcessStringLength * 2; + string2[kChildProcessStringLength] = '\0'; + + // A string that crosses from the mapped into the unmapped region + char* string3 = region + page_size - kChildProcessStringLength + 1; + string3[kChildProcessStringLength] = '\0'; + + // A string entirely in the unmapped region + char* string4 = region + page_size + 10; + string4[kChildProcessStringLength] = '\0'; + + strings->push_back(StringDataInChildProcess(string1)); + strings->push_back(StringDataInChildProcess(string2)); + strings->push_back(StringDataInChildProcess(string3)); + strings->push_back(StringDataInChildProcess(string4)); - // A string that crosses from the mapped into the unmapped region - string3_ = region + page_size_ - expected_length_ + 1; - string3_[expected_length_] = '\0'; + EXPECT_TRUE(pages->ResetAddrLen(region, page_size)); +} - // A string entirely in the unmapped region - string4_ = region + page_size_ + 10; - string4_[expected_length_] = '\0'; +CRASHPAD_CHILD_TEST_MAIN(ReadCStringUnmappedChildMain) { + ScopedMmap pages; + std::vector<StringDataInChildProcess> strings; + DoCStringUnmappedTestSetup(&pages, &strings); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + strings[0].Write(out); + strings[1].Write(out); + strings[2].Write(out); + strings[3].Write(out); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} - result_.reserve(expected_length_ + 1); +class ReadCStringUnmappedTest : public MultiprocessExec { + public: + ReadCStringUnmappedTest(bool limit_size) + : MultiprocessExec(), limit_size_(limit_size) { + SetChildTestMainFunction("ReadCStringUnmappedChildMain"); + } - EXPECT_TRUE(pages_.ResetAddrLen(region, page_size_)); + void RunAgainstSelf() { + ScopedMmap pages; + std::vector<StringDataInChildProcess> strings; + DoCStringUnmappedTestSetup(&pages, &strings); + DoTest(GetSelfProcess(), strings); } + void RunAgainstChild() { Run(); } + private: - void DoTest(pid_t pid) { - ProcessMemoryLinux memory; - ASSERT_TRUE(memory.Initialize(pid)); + void MultiprocessParent() override { + std::vector<StringDataInChildProcess> strings; + strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle())); + strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle())); + strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle())); + strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle())); + ASSERT_NO_FATAL_FAILURE(); + DoTest(ChildProcess(), strings); + } + + void DoTest(ProcessType process, + const std::vector<StringDataInChildProcess>& strings) { + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); + + std::string result; + result.reserve(kChildProcessStringLength + 1); if (limit_size_) { - ASSERT_TRUE(ReadCStringSizeLimited( - memory, string1_, expected_length_ + 1, &result_)); - EXPECT_EQ(result_, string1_); - ASSERT_TRUE(ReadCStringSizeLimited( - memory, string2_, expected_length_ + 1, &result_)); - EXPECT_EQ(result_, string2_); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, string3_, expected_length_ + 1, &result_)); - EXPECT_FALSE(ReadCStringSizeLimited( - memory, string4_, expected_length_ + 1, &result_)); + ASSERT_TRUE(memory.ReadCStringSizeLimited( + strings[0].address(), kChildProcessStringLength + 1, &result)); + EXPECT_EQ(result, strings[0].expected_value()); + ASSERT_TRUE(memory.ReadCStringSizeLimited( + strings[1].address(), kChildProcessStringLength + 1, &result)); + EXPECT_EQ(result, strings[1].expected_value()); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + strings[2].address(), kChildProcessStringLength + 1, &result)); + EXPECT_FALSE(memory.ReadCStringSizeLimited( + strings[3].address(), kChildProcessStringLength + 1, &result)); } else { - ASSERT_TRUE(ReadCString(memory, string1_, &result_)); - EXPECT_EQ(result_, string1_); - ASSERT_TRUE(ReadCString(memory, string2_, &result_)); - EXPECT_EQ(result_, string2_); - EXPECT_FALSE(ReadCString(memory, string3_, &result_)); - EXPECT_FALSE(ReadCString(memory, string4_, &result_)); + ASSERT_TRUE(memory.ReadCString(strings[0].address(), &result)); + EXPECT_EQ(result, strings[0].expected_value()); + ASSERT_TRUE(memory.ReadCString(strings[1].address(), &result)); + EXPECT_EQ(result, strings[1].expected_value()); + EXPECT_FALSE(memory.ReadCString(strings[2].address(), &result)); + EXPECT_FALSE(memory.ReadCString(strings[3].address(), &result)); } } - std::string result_; - ScopedMmap pages_; - const size_t page_size_; - const size_t region_size_; - static const size_t expected_length_ = 10; - char* string1_; - char* string2_; - char* string3_; - char* string4_; const bool limit_size_; DISALLOW_COPY_AND_ASSIGN(ReadCStringUnmappedTest); @@ -382,10 +580,10 @@ TEST(ProcessMemory, ReadCStringUnmappedSelf) { test.RunAgainstSelf(); } -TEST(ProcessMemory, ReadCStringUnmappedForked) { +TEST(ProcessMemory, ReadCStringUnmappedChild) { ReadCStringUnmappedTest test(/* limit_size= */ false); ASSERT_FALSE(testing::Test::HasFailure()); - test.RunAgainstForked(); + test.RunAgainstChild(); } TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) { @@ -394,10 +592,10 @@ TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) { test.RunAgainstSelf(); } -TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedForked) { +TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedChild) { ReadCStringUnmappedTest test(/* limit_size= */ true); ASSERT_FALSE(testing::Test::HasFailure()); - test.RunAgainstForked(); + test.RunAgainstChild(); } } // namespace diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.cc b/chromium/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.cc index 9aedb2999bd..797a3ac4f8e 100644 --- a/chromium/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.cc +++ b/chromium/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.cc @@ -18,7 +18,7 @@ #include "build/build_config.h" -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER) #include <stdlib.h> #elif defined(OS_WIN) #include <malloc.h> @@ -31,7 +31,7 @@ namespace { // library to do so. This works even if C++ exceptions are disabled, causing // program termination if uncaught. void ThrowBadAlloc() { -#if defined(OS_POSIX) +#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER) // This works with both libc++ and libstdc++. std::__throw_bad_alloc(); #elif defined(OS_WIN) diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h index 49479639941..dc3000b09e1 100644 --- a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h +++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h @@ -23,6 +23,9 @@ #include <dispatch/dispatch.h> #elif defined(OS_WIN) #include <windows.h> +#elif defined(OS_ANDROID) +#include <condition_variable> +#include <mutex> #else #include <semaphore.h> #endif @@ -77,6 +80,10 @@ class Semaphore { dispatch_semaphore_t semaphore_; #elif defined(OS_WIN) HANDLE semaphore_; +#elif defined(OS_ANDROID) + std::condition_variable cv_; + std::mutex mutex_; + int value_; #else sem_t semaphore_; #endif diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc index 59ed71f2c50..3875f3f4336 100644 --- a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc +++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc @@ -18,13 +18,51 @@ #include <math.h> #include <time.h> +#include <chrono> + #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "util/misc/time.h" namespace crashpad { -#if !defined(OS_MACOSX) +#if defined(OS_ANDROID) + +Semaphore::Semaphore(int value) : cv_(), mutex_(), value_(value) {} + +Semaphore::~Semaphore() = default; + +void Semaphore::Wait() { + std::unique_lock<std::mutex> lock(mutex_); + cv_.wait(lock, [this] { return this->value_ > 0; }); + --value_; +} + +bool Semaphore::TimedWait(double seconds) { + DCHECK_GE(seconds, 0.0); + + if (isinf(seconds)) { + Wait(); + return true; + } + + std::unique_lock<std::mutex> lock(mutex_); + if (!cv_.wait_for(lock, std::chrono::duration<double>(seconds), [this] { + return this->value_ > 0; + })) { + return false; + } + --value_; + return true; +} + +void Semaphore::Signal() { + std::lock_guard<std::mutex> lock(mutex_); + ++value_; + cv_.notify_one(); +} + +#elif !defined(OS_MACOSX) Semaphore::Semaphore(int value) { PCHECK(sem_init(&semaphore_, 0, value) == 0) << "sem_init"; @@ -65,6 +103,6 @@ void Semaphore::Signal() { PCHECK(sem_post(&semaphore_) == 0) << "sem_post"; } -#endif +#endif // OS_ANDROID } // namespace crashpad diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc index e1f3648c3b9..10f7546d6bd 100644 --- a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc +++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc @@ -41,7 +41,10 @@ TEST(Semaphore, TimedWait) { TEST(Semaphore, TimedWaitTimeout) { Semaphore semaphore(0); - EXPECT_FALSE(semaphore.TimedWait(0.01)); // 10ms + semaphore.Signal(); + constexpr double kTenMs = 0.01; + EXPECT_TRUE(semaphore.TimedWait(kTenMs)); + EXPECT_FALSE(semaphore.TimedWait(kTenMs)); } TEST(Semaphore, TimedWaitInfinite_0) { diff --git a/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc index 60be08b5b33..60c824834ac 100644 --- a/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc +++ b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc @@ -46,10 +46,6 @@ class ThreadLogMessagesMaster { private: ThreadLogMessagesMaster() { - DCHECK(!tls_.initialized()); - tls_.Initialize(nullptr); - DCHECK(tls_.initialized()); - DCHECK(!logging::GetLogMessageHandler()); logging::SetLogMessageHandler(LogMessageHandler); } @@ -62,7 +58,7 @@ class ThreadLogMessagesMaster { size_t message_start, const std::string& string) { std::vector<std::string>* log_messages = - reinterpret_cast<std::vector<std::string>*>(tls_.Get()); + reinterpret_cast<std::vector<std::string>*>(GetInstance()->tls_.Get()); if (log_messages) { log_messages->push_back(string); } @@ -72,15 +68,11 @@ class ThreadLogMessagesMaster { return false; } - static base::ThreadLocalStorage::StaticSlot tls_; + base::ThreadLocalStorage::Slot tls_; DISALLOW_COPY_AND_ASSIGN(ThreadLogMessagesMaster); }; -// static -base::ThreadLocalStorage::StaticSlot ThreadLogMessagesMaster::tls_ - = TLS_INITIALIZER; - } // namespace ThreadLogMessages::ThreadLogMessages() : log_messages_() { diff --git a/chromium/third_party/crashpad/crashpad/util/util.gyp b/chromium/third_party/crashpad/crashpad/util/util.gyp index 43b6e32b608..0000ee2d0d2 100644 --- a/chromium/third_party/crashpad/crashpad/util/util.gyp +++ b/chromium/third_party/crashpad/crashpad/util/util.gyp @@ -199,9 +199,11 @@ 'posix/signals.h', 'posix/symbolic_constants_posix.cc', 'posix/symbolic_constants_posix.h', + 'process/process_memory.cc', 'process/process_memory.h', 'process/process_memory_linux.cc', 'process/process_memory_linux.h', + 'process/process_memory_native.h', 'process/process_memory_range.cc', 'process/process_memory_range.h', 'stdlib/aligned_allocator.cc', diff --git a/chromium/third_party/crashpad/crashpad/util/win/nt_internals.h b/chromium/third_party/crashpad/crashpad/util/win/nt_internals.h index a2a88b77444..dad04056912 100644 --- a/chromium/third_party/crashpad/crashpad/util/win/nt_internals.h +++ b/chromium/third_party/crashpad/crashpad/util/win/nt_internals.h @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#ifndef CRASHPAD_UTIL_WIN_NT_INTERNALS_H_ +#define CRASHPAD_UTIL_WIN_NT_INTERNALS_H_ + #include <windows.h> #include <winternl.h> @@ -92,3 +95,5 @@ void RtlGetUnloadEventTraceEx(ULONG** element_size, void** event_trace); } // namespace crashpad + +#endif // CRASHPAD_UTIL_WIN_NT_INTERNALS_H_ |