Files
sousa-gecko/toolkit/mozapps/update/tests/unit_update_binary/xpcshell_base.toml
T
Yannis Juglaret 5ce901aa2d Bug 2024954 - Handle zucchini OOM and CHECK failures in the updater on Windows. r=bobowen,application-update-reviewers,cdupuis
Nightly monitoring of the zucchini rollout revealed a surge in
SERVICE_STILL_APPLYING_ON_FAILURE, READ_ERROR and WRITE_ERROR during
updates. This commit aims to address READ_ERROR and WRITE_ERROR on all
platforms, and to address SERVICE_STILL_APPLYING_ON_FAILURE on Windows.
Addressing SERVICE_STILL_APPLYING_ON_FAILURE on Linux and macOS is done
in the follow-up commit.

SERVICE_STILL_APPLYING_ON_FAILURE means the updater process exited
without writing update.status, likely crashing. There are two potential
sources of crashes in zucchini. First, Chromium CHECK macros call
ImmediateCrash() (int3/ud2), which kills the process before
WriteStatusFile can run. More likely, std::bad_alloc from non-fallible
allocations in zucchini's disassembler (e.g. std::make_unique,
std::deque::push_back when parsing a large PE like xul.dll) would also
crash the process. Both cases can be caught by via SEH on Windows.

READ_ERROR and WRITE_ERROR can mask OOMs from CreateFileMapping /
MapViewOfFile failures (or mmap on POSIX) when zucchini tries to
memory-map large files. Unlike bspatch which uses sequential I/O,
zucchini memory-maps both the patch file and the output file, requiring
significantly more virtual address space.

This commit:

1. Adds an SEH exception filter (FilterZucchiniException) around
   zucchini's Load and ApplyUnsafe calls that catches
   EXCEPTION_IN_PAGE_ERROR (existing), EXCEPTION_BREAKPOINT /
   EXCEPTION_ILLEGAL_INSTRUCTION (CHECK failures), and 0xE06D7363
   (std::bad_alloc). All are recoverable exceptions where the process
   state is sound. Exceptions indicating corrupt state
   (EXCEPTION_ACCESS_VIOLATION, etc.) are left unhandled. This improves
   crash recovery and OOM detection on Windows.

2. Introduces kStatusOutOfMemory in zucchini's status codes, mapped to
   BSPATCH_MEM_ERROR (12) in the updater. OOMs from the SEH exception
   path and from the mapping-failure path (via is_oom() from the
   previous commit) both flow through this code, which lets the update
   service knows that we are currently under memory pressure.

3. Eagerly resets mPatchFileDecoder in PatchFile::Execute() after Apply
   returns, so memory-mapped patch files are released between sequential
   patch actions rather than accumulating until the ActionList is
   destroyed.

Differential Revision: https://phabricator.services.mozilla.com/D288918
2026-06-10 11:43:59 +00:00

307 lines
5.6 KiB
TOML

[DEFAULT]
run-if = [
"os != 'android'",
]
tags = "appupdate base"
head = "head_no_service.js"
skip-if = [
"msix", # Our updater is disabled in MSIX builds
"os == 'win' && ccov",
]
support-files = [
"../data/shared.js",
"../data/sharedUpdateXML.js",
"../data/xpcshellUtilsAUS.js",
]
["invalidArgCallbackFileNotInInstallDirFailure.js"]
["invalidArgCallbackFilePathTooLongFailure.js"]
["invalidArgInstallDirPathTooLongFailure.js"]
["invalidArgInstallDirPathTraversalFailure.js"]
["invalidArgInstallWorkingDirPathNotSameFailure_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["invalidArgPatchDirPathTraversalFailure.js"]
["invalidArgStageDirNotInInstallDirFailure_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["invalidArgWorkingDirPathLocalUNCFailure_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["invalidArgWorkingDirPathRelativeFailure.js"]
["marAppApplyDirLockedStageFailure_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marAppApplyUpdateAppBinInUseStageSuccess_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marAppApplyUpdateSkippedWriteAccess_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marAppApplyUpdateStageOldVersionFailure.js"]
["marAppApplyUpdateStageSuccess.js"]
["marAppApplyUpdateSuccess.js"]
["marAppInUseBackgroundTaskFailure_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marAppInUseStageFailureComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marAppInUseStageSuccessComplete_unix.js"]
run-if = [
"os != 'win'", # not a Windows test
]
["marAppInUseSuccessComplete.js"]
["marCallbackAppStageSuccessComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marCallbackAppStageSuccessPartialZucchini_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marCallbackAppStageSuccessPartial_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marCallbackAppSuccessComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marCallbackAppSuccessPartialZucchini_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marCallbackAppSuccessPartial_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marCallbackUmask_unix.js"]
run-if = [
"os != 'win'", # not a Windows test
]
reason = "Unix only test"
["marFailurePartial.js"]
["marFailurePartialZucchini.js"]
["marFailurePartialZucchiniBadAlloc_win.js"]
skip-if = [
"os != 'win'",
"tsan", # TSan intercepts operator new and aborts on OOM before bad_alloc
"asan", # Same issue with ASan
]
reason = "Windows only test, POSIX planned for bug 2043122"
["marFailurePartialZucchiniCheck_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test, POSIX planned for bug 2043122"
["marFailurePartialZucchiniCheckDestructor_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test, POSIX planned for bug 2043122"
["marFileInUseStageFailureComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileInUseStageFailurePartialZucchini_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileInUseStageFailurePartial_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileInUseSuccessComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileInUseSuccessPartialZucchini_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileInUseSuccessPartial_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileLockedFailureComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileLockedFailurePartialZucchini_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileLockedFailurePartial_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileLockedStageFailureComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileLockedStageFailurePartialZucchini_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marFileLockedStageFailurePartial_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marMissingUpdateSettings.js"]
["marMissingUpdateSettingsStage.js"]
["marPIDPersistsSuccessComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marPostUpdateEnvironment.js"]
run-if = [
"os == 'mac'",
"os == 'win'",
]
reason = "Post-update only supported on Windows and macOS"
["marRMRFDirFileInUseStageFailureComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marRMRFDirFileInUseStageFailurePartialZucchini_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marRMRFDirFileInUseStageFailurePartial_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marRMRFDirFileInUseSuccessComplete_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marRMRFDirFileInUseSuccessPartialZucchini_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marRMRFDirFileInUseSuccessPartial_win.js"]
run-if = [
"os == 'win'",
]
reason = "Windows only test"
["marStageFailurePartial.js"]
["marStageFailurePartialZucchini.js"]
["marStageSuccessComplete.js"]
run-sequentially = ["os == 'linux'"] # frequent fail in parallel on Linux
["marStageSuccessPartial.js"]
["marStageSuccessPartialZucchini.js"]
["marSuccessComplete.js"]
["marSuccessPartial.js"]
["marSuccessPartialWhileBackgroundTaskRunning.js"]
["marSuccessPartialZucchini.js"]
["marSuccessPartialZucchiniWhileBackgroundTaskRunning.js"]
["marVersionDowngrade.js"]
["marWrongChannel.js"]
["marWrongChannelStage.js"]