mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-21 09:49:14 +00:00
WIP: Bug 1723204: Infrastructure for causing a crash at most N times r=KrisWright,ckerschb,freddyb
Differential Revision: https://phabricator.services.mozilla.com/D121416
This commit is contained in:
parent
2312e1ba72
commit
15cda29aa2
@ -202,6 +202,66 @@ nsresult RegexEval(const nsAString& aPattern, const nsAString& aString,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* MOZ_CRASH_UNSAFE_PRINTF has a sPrintfCrashReasonSize-sized buffer. We need
|
||||
* to make sure we don't exceed it. These functions perform this check and
|
||||
* munge things for us.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Destructively truncates a string to fit within the limit
|
||||
*/
|
||||
char* nsContentSecurityUtils::SmartFormatCrashString(const char* str) {
|
||||
return nsContentSecurityUtils::SmartFormatCrashString(strdup(str));
|
||||
}
|
||||
|
||||
char* nsContentSecurityUtils::SmartFormatCrashString(char* str) {
|
||||
auto str_len = strlen(str);
|
||||
|
||||
if (str_len > sPrintfCrashReasonSize) {
|
||||
str[sPrintfCrashReasonSize - 1] = '\0';
|
||||
str_len = strlen(str);
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(sPrintfCrashReasonSize > str_len);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destructively truncates two strings to fit within the limit.
|
||||
* format_string is a format string containing two %s entries
|
||||
* The second string will be truncated to the _last_ 25 characters
|
||||
* The first string will be truncated to the remaining limit.
|
||||
*/
|
||||
nsCString nsContentSecurityUtils::SmartFormatCrashString(
|
||||
const char* part1, const char* part2, const char* format_string) {
|
||||
return SmartFormatCrashString(strdup(part1), strdup(part2), format_string);
|
||||
}
|
||||
|
||||
nsCString nsContentSecurityUtils::SmartFormatCrashString(
|
||||
char* part1, char* part2, const char* format_string) {
|
||||
auto part1_len = strlen(part1);
|
||||
auto part2_len = strlen(part2);
|
||||
|
||||
auto constant_len = strlen(format_string) - 4;
|
||||
|
||||
if (part1_len + part2_len + constant_len > sPrintfCrashReasonSize) {
|
||||
if (part2_len > 25) {
|
||||
part2 += (part2_len - 25);
|
||||
}
|
||||
part2_len = strlen(part2);
|
||||
|
||||
part1[sPrintfCrashReasonSize - (constant_len + part2_len + 1)] = '\0';
|
||||
part1_len = strlen(part1);
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(sPrintfCrashReasonSize >
|
||||
constant_len + part1_len + part2_len);
|
||||
|
||||
auto parts = nsPrintfCString(format_string, part1, part2);
|
||||
return std::move(parts);
|
||||
}
|
||||
|
||||
/*
|
||||
* Telemetry Events extra data only supports 80 characters, so we optimize the
|
||||
* filename to be smaller and collect more data.
|
||||
@ -405,6 +465,56 @@ FilenameTypeAndDetails nsContentSecurityUtils::FilenameToFilenameType(
|
||||
return FilenameTypeAndDetails(kOther, Nothing());
|
||||
}
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// Crash String must be safe from a telemetry point of view.
|
||||
// This will be ensured when this function is used.
|
||||
void PossiblyCrash(const char* pref_suffix, const nsCString crash_string) {
|
||||
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
|
||||
// We only crash in the parent (unfortunately) because it's
|
||||
// the only place we can be sure that our only-crash-once
|
||||
// pref-writing works.
|
||||
return;
|
||||
}
|
||||
|
||||
nsCString previous_crashes("security.crash_tracking.");
|
||||
previous_crashes.Append(pref_suffix);
|
||||
previous_crashes.Append(".prevCrashes");
|
||||
|
||||
nsCString max_crashes("security.crash_tracking.");
|
||||
max_crashes.Append(pref_suffix);
|
||||
max_crashes.Append(".maxCrashes");
|
||||
|
||||
int32_t numberOfPreviousCrashes = 0;
|
||||
numberOfPreviousCrashes = Preferences::GetInt(previous_crashes.get(), 0);
|
||||
|
||||
int32_t maxAllowableCrashes = 0;
|
||||
maxAllowableCrashes = Preferences::GetInt(max_crashes.get(), 0);
|
||||
|
||||
if (numberOfPreviousCrashes >= maxAllowableCrashes) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv =
|
||||
Preferences::SetInt(previous_crashes.get(), ++numberOfPreviousCrashes);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrefService> prefsCom = Preferences::GetService();
|
||||
Preferences* prefs = static_cast<Preferences*>(prefsCom.get());
|
||||
|
||||
if (!prefs->AllowOffMainThreadSave()) {
|
||||
// Do not crash if we can't save prefs off the main thread
|
||||
return;
|
||||
}
|
||||
|
||||
rv = prefs->SavePrefFileBlocking();
|
||||
if (!NS_FAILED(rv)) {
|
||||
MOZ_CRASH_UNSAFE_PRINTF("%s", nsContentSecurityUtils::SmartFormatCrashString(crash_string.get()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
class EvalUsageNotificationRunnable final : public Runnable {
|
||||
public:
|
||||
EvalUsageNotificationRunnable(bool aIsSystemPrincipal,
|
||||
@ -570,22 +680,13 @@ bool nsContentSecurityUtils::IsEvalAllowed(JSContext* cx,
|
||||
fileName.get(), NS_ConvertUTF16toUTF8(aScript).get()));
|
||||
|
||||
// Maybe Crash
|
||||
#ifdef DEBUG
|
||||
// MOZ_CRASH_UNSAFE_PRINTF gives us at most 1024 characters to print.
|
||||
// The given string literal leaves us with ~950, so I'm leaving
|
||||
// each 475 for fileName and aScript each.
|
||||
if (fileName.Length() > 475) {
|
||||
fileName.SetLength(475);
|
||||
}
|
||||
nsAutoCString trimmedScript = NS_ConvertUTF16toUTF8(aScript);
|
||||
if (trimmedScript.Length() > 475) {
|
||||
trimmedScript.SetLength(475);
|
||||
}
|
||||
MOZ_CRASH_UNSAFE_PRINTF(
|
||||
"Blocking eval() %s from file %s and script provided "
|
||||
"%s",
|
||||
(aIsSystemPrincipal ? "with System Principal" : "in parent process"),
|
||||
fileName.get(), trimmedScript.get());
|
||||
#if defined(DEBUG)
|
||||
auto crashString = nsContentSecurityUtils::SmartFormatCrashString(
|
||||
NS_ConvertUTF16toUTF8(aScript).get(), fileName.get(),
|
||||
(aIsSystemPrincipal
|
||||
? "Blocking eval() with System Principal with script %s from file %s"
|
||||
: "Blocking eval() in parent process with script %s from file %s"));
|
||||
MOZ_CRASH_UNSAFE_PRINTF("%s", crashString.get());
|
||||
#endif
|
||||
|
||||
return false;
|
||||
|
@ -37,8 +37,6 @@ class nsContentSecurityUtils {
|
||||
static bool IsConsideredSameOriginForUIR(nsIPrincipal* aTriggeringPrincipal,
|
||||
nsIPrincipal* aResultPrincipal);
|
||||
|
||||
static FilenameTypeAndDetails FilenameToFilenameType(
|
||||
const nsString& fileName, bool collectAdditionalExtensionData);
|
||||
static bool IsEvalAllowed(JSContext* cx, bool aIsSystemPrincipal,
|
||||
const nsAString& aScript);
|
||||
static void NotifyEvalUsage(bool aIsSystemPrincipal,
|
||||
@ -67,6 +65,16 @@ class nsContentSecurityUtils {
|
||||
static long ClassifyDownload(nsIChannel* aChannel,
|
||||
const nsAutoCString& aMimeTypeGuess);
|
||||
|
||||
// Public only for testing
|
||||
static FilenameTypeAndDetails FilenameToFilenameType(
|
||||
const nsString& fileName, bool collectAdditionalExtensionData);
|
||||
static char* SmartFormatCrashString(const char* str);
|
||||
static char* SmartFormatCrashString(char* str);
|
||||
static nsCString SmartFormatCrashString(const char* part1, const char* part2,
|
||||
const char* format_string);
|
||||
static nsCString SmartFormatCrashString(char* part1, char* part2,
|
||||
const char* format_string);
|
||||
|
||||
#if defined(DEBUG)
|
||||
static void AssertAboutPageHasCSP(mozilla::dom::Document* aDocument);
|
||||
#endif
|
||||
|
43
dom/security/test/gtest/TestSmartCrashTrimmer.cpp
Normal file
43
dom/security/test/gtest/TestSmartCrashTrimmer.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nsContentSecurityUtils.h"
|
||||
#include "nsTString.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
#define ASSERT_STRCMP(first, second) ASSERT_TRUE(strcmp(first, second) == 0);
|
||||
|
||||
#define ASSERT_STRCMP_AND_PRINT(first, second) \
|
||||
fprintf(stderr, "First: %s\n", first); \
|
||||
fprintf(stderr, "Second: %s\n", second); \
|
||||
fprintf(stderr, "strcmp = %i\n", strcmp(first, second)); \
|
||||
ASSERT_EQUAL(first, second);
|
||||
|
||||
TEST(SmartCrashTrimmer, Test)
|
||||
{
|
||||
static_assert(sPrintfCrashReasonSize == 1024);
|
||||
{
|
||||
auto ret = nsContentSecurityUtils::SmartFormatCrashString(
|
||||
std::string(1025, '.').c_str());
|
||||
ASSERT_EQ(strlen(ret), 1023ul);
|
||||
}
|
||||
|
||||
{
|
||||
auto ret = nsContentSecurityUtils::SmartFormatCrashString(
|
||||
std::string(1025, '.').c_str(), std::string(1025, 'A').c_str(),
|
||||
"Hello %s world %s!");
|
||||
char expected[1025];
|
||||
sprintf(expected, "Hello %s world AAAAAAAAAAAAAAAAAAAAAAAAA!",
|
||||
std::string(984, '.').c_str());
|
||||
ASSERT_STRCMP(ret.get(), expected);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ UNIFIED_SOURCES += [
|
||||
"TestCSPParser.cpp",
|
||||
"TestFilenameEvalParser.cpp",
|
||||
"TestSecureContext.cpp",
|
||||
"TestSmartCrashTrimmer.cpp",
|
||||
]
|
||||
|
||||
if CONFIG["OS_TARGET"] != "Android":
|
||||
|
@ -421,6 +421,9 @@ class Preferences final : public nsIPrefService,
|
||||
nsresult SavePrefFileBlocking();
|
||||
nsresult SavePrefFileAsynchronous();
|
||||
|
||||
// If this is false, only blocking writes, on main thread are allowed.
|
||||
bool AllowOffMainThreadSave();
|
||||
|
||||
private:
|
||||
virtual ~Preferences();
|
||||
|
||||
@ -442,9 +445,6 @@ class Preferences final : public nsIPrefService,
|
||||
nsresult SavePrefFileInternal(nsIFile* aFile, SaveMethod aSaveMethod);
|
||||
nsresult WritePrefFile(nsIFile* aFile, SaveMethod aSaveMethod);
|
||||
|
||||
// If this is false, only blocking writes, on main thread are allowed.
|
||||
bool AllowOffMainThreadSave();
|
||||
|
||||
// Helpers for implementing
|
||||
// Register(Prefix)Callback/Unregister(Prefix)Callback.
|
||||
public:
|
||||
|
Loading…
x
Reference in New Issue
Block a user