Skip to content

Commit

Permalink
Bug 1847098 - Report library version from filename at crash r=gsvelto
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandre Lissy committed Feb 28, 2024
1 parent 8a3bc14 commit 0cf18a2
Show file tree
Hide file tree
Showing 12 changed files with 320 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}

Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
62 changes: 62 additions & 0 deletions toolkit/crashreporter/linux_utils.cc
Original file line number Diff line number Diff line change
@@ -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;
}
18 changes: 18 additions & 0 deletions toolkit/crashreporter/linux_utils.h
Original file line number Diff line number Diff line change
@@ -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__ */
8 changes: 8 additions & 0 deletions toolkit/crashreporter/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
146 changes: 146 additions & 0 deletions toolkit/crashreporter/test/gtest/TestElfSoVersion.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
16 changes: 16 additions & 0 deletions toolkit/crashreporter/test/gtest/moz.build
Original file line number Diff line number Diff line change
@@ -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"
4 changes: 4 additions & 0 deletions toolkit/crashreporter/test/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -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 += [
Expand Down
33 changes: 33 additions & 0 deletions toolkit/crashreporter/test/unit/test_crash_modules_linux.js
Original file line number Diff line number Diff line change
@@ -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
);
});
Loading

0 comments on commit 0cf18a2

Please sign in to comment.