Bug 1847098 - Report library version from filename at crash r=gsvelto

Differential Revision: https://phabricator.services.mozilla.com/D202249
This commit is contained in:
Alexandre Lissy 2024-02-28 22:38:44 +00:00
parent 9d1edf90f0
commit 636e377cbf
13 changed files with 325 additions and 8 deletions

View file

@ -466,8 +466,8 @@ class MicrodumpWriter {
char file_name[NAME_MAX];
char file_path[NAME_MAX];
dumper_->GetMappingEffectiveNameAndPath(
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
dumper_->GetMappingEffectiveNamePathAndVersion(
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name), nullptr);
LogAppend("M ");
LogAppend(static_cast<uintptr_t>(mapping.start_addr));

View file

@ -468,11 +468,12 @@ bool ElfFileSoName(const LinuxDumper& dumper,
} // namespace
void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
void LinuxDumper::GetMappingEffectiveNamePathAndVersion(const MappingInfo& mapping,
char* file_path,
size_t file_path_size,
char* file_name,
size_t file_name_size) {
size_t file_name_size,
VersionComponents* version) {
my_strlcpy(file_path, mapping.name, file_path_size);
// Tools such as minidump_stackwalk use the name of the module to look up
@ -487,6 +488,9 @@ void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
const char* basename = my_strrchr(file_path, '/');
basename = basename == NULL ? file_path : (basename + 1);
my_strlcpy(file_name, basename, file_name_size);
if (version) {
ElfFileSoVersion(mapping.name, version);
}
return;
}
@ -512,6 +516,10 @@ void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
my_strlcpy(file_path, file_name, file_path_size);
}
}
if (version) {
ElfFileSoVersion(mapping.name, version);
}
}
bool LinuxDumper::ReadAuxv() {

View file

@ -56,6 +56,10 @@
#include "common/memory_allocator.h"
#include "google_breakpad/common/minidump_format.h"
#if defined(XP_LINUX)
# include "linux_utils.h"
#endif // defined(XP_LINUX)
namespace google_breakpad {
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
@ -213,11 +217,12 @@ class LinuxDumper {
// other cases, however, a library can be mapped from an archive (e.g., when
// loading .so libs from an apk on Android) and this method is able to
// reconstruct the original file name.
void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
void GetMappingEffectiveNamePathAndVersion(const MappingInfo& mapping,
char* file_path,
size_t file_path_size,
char* file_name,
size_t file_name_size);
size_t file_name_size,
VersionComponents* version);
protected:
bool ReadAuxv();

View file

@ -717,8 +717,16 @@ class MinidumpWriter {
char file_name[NAME_MAX];
char file_path[NAME_MAX];
dumper_->GetMappingEffectiveNameAndPath(
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
VersionComponents version;
dumper_->GetMappingEffectiveNamePathAndVersion(
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name), &version);
mod->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
mod->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
mod->version_info.file_version_hi = version.size() >= 1 ? version[0] : 0;
mod->version_info.file_version_lo = version.size() >= 2 ? version[1] : 0;
mod->version_info.product_version_hi = version.size() >= 3 ? version[2] : 0;
mod->version_info.product_version_lo = version.size() == 4 ? version[3] : 0;
MDLocationDescriptor ld;
if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld))

View file

@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "linux_utils.h"
bool ElfFileSoVersion(const char* mapping_name,
VersionComponents* version_components) {
if (!version_components) {
return false;
}
std::string path = std::string(mapping_name);
std::string filename = path.substr(path.find_last_of('/') + 1);
std::string dot_so_dot(".so.");
// We found no version so just report 0
size_t has_dot_so_dot = filename.find(dot_so_dot);
if (has_dot_so_dot == std::string::npos) {
return true;
}
std::string so_version =
filename.substr(has_dot_so_dot + dot_so_dot.length());
std::string tmp;
for (std::string::iterator it = so_version.begin(); it != so_version.end();
++it) {
// We can't have more than four: MAJOR.minor.release.patch
if (version_components->size() == 4) {
break;
}
char c = *it;
if (c != '.') {
if (isdigit(c)) {
tmp += c;
}
if (std::next(it) != so_version.end()) {
continue;
}
}
if (tmp.length() > 0) {
int t = std::stoi(tmp); // assume tmp is < UINT32T_MAX
if (t < 0) {
return false;
}
uint32_t casted_tmp = static_cast<uint32_t>(t);
// We have lost some information we should warn.
if ((unsigned int)t != casted_tmp) {
return false;
}
version_components->push_back(casted_tmp);
}
tmp = "";
}
return true;
}

View file

@ -0,0 +1,18 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef toolkit_breakpad_linux_utils_h__
#define toolkit_breakpad_linux_utils_h__
#include <stdio.h>
#include <stdint.h>
#include <string>
#include <vector>
using VersionComponents = std::vector<uint32_t>;
bool ElfFileSoVersion(const char* mapping_name, VersionComponents* version);
#endif /* toolkit_breakpad_linux_utils_h__ */

View file

@ -55,6 +55,14 @@ if CONFIG["MOZ_CRASHREPORTER"]:
"google-breakpad/src/processor",
]
UNIFIED_SOURCES += [
"linux_utils.cc",
]
EXPORTS += [
"linux_utils.h",
]
if CONFIG["MOZ_OXIDIZED_BREAKPAD"]:
DIRS += ["rust_minidump_writer_linux"]

View file

@ -0,0 +1,146 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "linux_utils.h"
#define ASSERT_EQ_UNSIGNED(v, e) ASSERT_EQ((v), (uint32_t)(e))
using namespace mozilla;
class CrashReporter : public ::testing::Test {};
TEST_F(CrashReporter, ElfSoNoVersion) {
VersionComponents version;
bool rv = ElfFileSoVersion("libdbus1.so", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 0);
}
TEST_F(CrashReporter, ElfSo6) {
VersionComponents version;
bool rv = ElfFileSoVersion("libm.so.6", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 1);
ASSERT_EQ_UNSIGNED(version[0], 6);
}
TEST_F(CrashReporter, ElfSoNormalShort) {
VersionComponents version;
bool rv = ElfFileSoVersion("libdbus1.so.1.2", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 2);
ASSERT_EQ_UNSIGNED(version[0], 1);
ASSERT_EQ_UNSIGNED(version[1], 2);
}
TEST_F(CrashReporter, ElfSoNormalComplete) {
VersionComponents version;
bool rv = ElfFileSoVersion("libdbus1.so.1.2.3", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 3);
ASSERT_EQ_UNSIGNED(version[0], 1);
ASSERT_EQ_UNSIGNED(version[1], 2);
ASSERT_EQ_UNSIGNED(version[2], 3);
}
TEST_F(CrashReporter, ElfSoNormalPrerelease) {
VersionComponents version;
bool rv = ElfFileSoVersion("libdbus1.so.1.2.3.98", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 4);
ASSERT_EQ_UNSIGNED(version[0], 1);
ASSERT_EQ_UNSIGNED(version[1], 2);
ASSERT_EQ_UNSIGNED(version[2], 3);
ASSERT_EQ_UNSIGNED(version[3], 98);
}
TEST_F(CrashReporter, ElfSoNormalPrereleaseToomuch) {
VersionComponents version;
bool rv = ElfFileSoVersion("libdbus1.so.1.2.3.98.9.2.3", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 4);
ASSERT_EQ_UNSIGNED(version[0], 1);
ASSERT_EQ_UNSIGNED(version[1], 2);
ASSERT_EQ_UNSIGNED(version[2], 3);
ASSERT_EQ_UNSIGNED(version[3], 98);
}
TEST_F(CrashReporter, ElfSoBig) {
VersionComponents version;
bool rv = ElfFileSoVersion("libatk-1.0.so.0.25009.1", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 3);
ASSERT_EQ_UNSIGNED(version[0], 0);
ASSERT_EQ_UNSIGNED(version[1], 25009);
ASSERT_EQ_UNSIGNED(version[2], 1);
}
TEST_F(CrashReporter, ElfSoCairo) {
VersionComponents version;
bool rv = ElfFileSoVersion("libcairo.so.2.11800.3", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 3);
ASSERT_EQ_UNSIGNED(version[0], 2);
ASSERT_EQ_UNSIGNED(version[1], 11800);
ASSERT_EQ_UNSIGNED(version[2], 3);
}
TEST_F(CrashReporter, ElfSoMax) {
VersionComponents version;
bool rv = ElfFileSoVersion(
"libcairo.so.2147483647.2147483647.2147483647.2147483647", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 4);
ASSERT_EQ_UNSIGNED(version[0], INT32_MAX);
ASSERT_EQ_UNSIGNED(version[1], INT32_MAX);
ASSERT_EQ_UNSIGNED(version[2], INT32_MAX);
ASSERT_EQ_UNSIGNED(version[3], INT32_MAX);
}
TEST_F(CrashReporter, ElfSoTimestamp) {
VersionComponents version;
bool rv = ElfFileSoVersion("libabsl_time_zone.so.20220623.0.0", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 3);
ASSERT_EQ_UNSIGNED(version[0], 20220623);
ASSERT_EQ_UNSIGNED(version[1], 0);
ASSERT_EQ_UNSIGNED(version[2], 0);
}
TEST_F(CrashReporter, ElfSoChars) {
VersionComponents version;
bool rv = ElfFileSoVersion("libabsl_time_zone.so.1.2.3rc4", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 3);
ASSERT_EQ_UNSIGNED(version[0], 1);
ASSERT_EQ_UNSIGNED(version[1], 2);
ASSERT_EQ_UNSIGNED(version[2], 34);
}
TEST_F(CrashReporter, ElfSoCharsMore) {
VersionComponents version;
bool rv = ElfFileSoVersion("libabsl_time_zone.so.1.2.3rc4.9", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 4);
ASSERT_EQ_UNSIGNED(version[0], 1);
ASSERT_EQ_UNSIGNED(version[1], 2);
ASSERT_EQ_UNSIGNED(version[2], 34);
ASSERT_EQ_UNSIGNED(version[3], 9);
}
TEST_F(CrashReporter, ElfSoCharsOnly) {
VersionComponents version;
bool rv = ElfFileSoVersion("libabsl_time_zone.so.final", &version);
ASSERT_TRUE(rv);
ASSERT_EQ_UNSIGNED(version.size(), 0);
}
TEST_F(CrashReporter, ElfSoNullVersion) {
bool rv = ElfFileSoVersion("libabsl_time_zone.so.1", nullptr);
ASSERT_FALSE(rv);
}

View file

@ -0,0 +1,16 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at http://mozilla.org/MPL/2.0/.
Library("crashreportertest")
if CONFIG["OS_ARCH"] == "Linux":
UNIFIED_SOURCES = [
"TestElfSoVersion.cpp",
]
include("/ipc/chromium/chromium-config.mozbuild")
FINAL_LIBRARY = "xul-gtest"

View file

@ -9,6 +9,10 @@ XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell.toml", "unit_ipc/xpcshell.toml"]
if CONFIG["MOZ_PHC"]:
XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell-phc.toml", "unit_ipc/xpcshell-phc.toml"]
TEST_DIRS += [
"gtest",
]
BROWSER_CHROME_MANIFESTS += ["browser/browser.toml"]
UNIFIED_SOURCES += [

View file

@ -0,0 +1,33 @@
add_task(async function run_test() {
if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
dump(
"INFO | test_crash_modules.js | Can't test crashreporter in a non-libxul build.\n"
);
return;
}
await do_crash(
function () {
crashType = CrashTestUtils.CRASH_ABORT;
},
async function (mdump, extra, extraFile) {
runMinidumpAnalyzer(mdump);
// Refresh updated extra data
extra = await IOUtils.readJSON(extraFile.path);
// Check modules' versions
const modules = extra.StackTraces.modules;
const version_regexp = /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/;
for (let module of modules) {
console.debug("module", module);
console.debug("version => ", module.version);
console.debug("version regex => ", version_regexp.exec(module.version));
Assert.notEqual(version_regexp.exec(module.version), null);
}
},
// process will exit with a zero exit status
true
);
});

View file

@ -40,6 +40,10 @@ run-if = ["os == 'win'"]
reason = "Test covering Windows-specific module handling"
run-sequentially = "very high failure rate in parallel"
["test_crash_modules_linux.js"]
run-if = ["os == 'linux'"]
reason = "Test covering Linux-specific module handling"
["test_crash_moz_crash.js"]
["test_crash_oom.js"]

View file

@ -19,9 +19,14 @@ UNIFIED_SOURCES += [
"/toolkit/crashreporter/breakpad-client/linux/minidump_writer/linux_ptrace_dumper.cc",
"/toolkit/crashreporter/breakpad-client/linux/minidump_writer/minidump_writer.cc",
"/toolkit/crashreporter/breakpad-client/minidump_file_writer.cc",
"/toolkit/crashreporter/linux_utils.cc",
"injector.cc",
]
LOCAL_INCLUDES += [
"/toolkit/crashreporter/",
]
USE_LIBS += [
"breakpad_common_s",
"breakpad_linux_common_s",