From a8571576a1813be164da0fc91861e5469c42b046 Mon Sep 17 00:00:00 2001 From: John Schanck Date: Sun, 26 Apr 2026 09:18:06 +0200 Subject: [PATCH 01/12] Bug 2030135 - improve error handling in PK11_ImportPrivateKeyInfoAndReturnKey --- security/nss/lib/pk11wrap/pk11pk12.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/security/nss/lib/pk11wrap/pk11pk12.c b/security/nss/lib/pk11wrap/pk11pk12.c index 4f8f9d2518..de86dbe752 100644 --- a/security/nss/lib/pk11wrap/pk11pk12.c +++ b/security/nss/lib/pk11wrap/pk11pk12.c @@ -650,12 +650,15 @@ PK11_ImportPrivateKeyInfoAndReturnKey(PK11SlotInfo *slot, rv = PK11_ImportAndReturnPrivateKey(slot, lpk, nickname, publicValue, isPerm, isPrivate, keyUsage, privk, wincx); -loser: - if (arena != NULL) { - PORT_FreeArena(arena, PR_TRUE); + if (rv != SECSuccess) { + goto loser; } + PORT_FreeArena(arena, PR_TRUE); + return SECSuccess; - return rv; +loser: + PORT_FreeArena(arena, PR_TRUE); + return SECFailure; } SECStatus From 21d1a7dc24ffde477181051b842a6eb7a2f79a8a Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sun, 26 Apr 2026 10:06:09 +0200 Subject: [PATCH 02/12] [DOM/media] Use extended channel data as-appropriate. --- dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp index 0a2e758ccf..f640aa3ab6 100644 --- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp @@ -67,7 +67,7 @@ CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames) } else if (aFrame->format == AV_SAMPLE_FMT_FLTP) { // Planar audio data. Pack it into something we can understand. AudioDataValue* tmp = audio.get(); - AudioDataValue** data = reinterpret_cast(aFrame->data); + AudioDataValue** data = reinterpret_cast(aFrame->extended_data); for (uint32_t frame = 0; frame < aNumAFrames; frame++) { for (uint32_t channel = 0; channel < aNumChannels; channel++) { *tmp++ = data[channel][frame]; @@ -86,7 +86,7 @@ CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames) // Planar audio data. Convert it from S16 to 32 bits float // and pack it into something we can understand. AudioDataValue* tmp = audio.get(); - int16_t** data = reinterpret_cast(aFrame->data); + int16_t** data = reinterpret_cast(aFrame->extended_data); for (uint32_t frame = 0; frame < aNumAFrames; frame++) { for (uint32_t channel = 0; channel < aNumChannels; channel++) { *tmp++ = AudioSampleToFloat(data[channel][frame]); @@ -105,7 +105,7 @@ CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames) // Planar audio data. Convert it from S32 to 32 bits float // and pack it into something we can understand. AudioDataValue* tmp = audio.get(); - int32_t** data = reinterpret_cast(aFrame->data); + int32_t** data = reinterpret_cast(aFrame->extended_data); for (uint32_t frame = 0; frame < aNumAFrames; frame++) { for (uint32_t channel = 0; channel < aNumChannels; channel++) { *tmp++ = AudioSampleToFloat(data[channel][frame]); From f897efd8029b797109cb131872fc0f5f237c9e4b Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sun, 26 Apr 2026 10:13:02 +0200 Subject: [PATCH 03/12] [DOM] Add some death grips to TextTrackManager. --- dom/html/TextTrackManager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dom/html/TextTrackManager.cpp b/dom/html/TextTrackManager.cpp index 4b69675a0d..b68fdb6aca 100644 --- a/dom/html/TextTrackManager.cpp +++ b/dom/html/TextTrackManager.cpp @@ -211,6 +211,7 @@ TextTrackManager::AddCues(TextTrack* aTextTrack) for (uint32_t i = 0; i < cueList->Length(); ++i) { mNewCues->AddCue(*cueList->IndexedGetter(i, dummy)); } + RefPtr kungFuDeathGrip(this); DispatchTimeMarchesOn(); } } @@ -236,6 +237,7 @@ TextTrackManager::RemoveTextTrack(TextTrack* aTextTrack, bool aPendingListOnly) for (uint32_t i = 0; i < removeCueList->Length(); ++i) { mNewCues->RemoveCue(*((*removeCueList)[i])); } + RefPtr kungFuDeathGrip(this); DispatchTimeMarchesOn(); } } @@ -305,6 +307,7 @@ TextTrackManager::NotifyCueAdded(TextTrackCue& aCue) if (mNewCues) { mNewCues->AddCue(aCue); } + RefPtr kungFuDeathGrip(this); DispatchTimeMarchesOn(); } @@ -315,6 +318,7 @@ TextTrackManager::NotifyCueRemoved(TextTrackCue& aCue) if (mNewCues) { mNewCues->RemoveCue(aCue); } + RefPtr kungFuDeathGrip(this); DispatchTimeMarchesOn(); if (aCue.GetActive()) { // We remove an active cue, need to update the display. @@ -824,6 +828,7 @@ TextTrackManager::NotifyCueUpdated(TextTrackCue *aCue) { // TODO: Add/Reorder the cue to mNewCues if we have some optimization? WEBVTT_LOG("NotifyCueUpdated"); + RefPtr kungFuDeathGrip(this); DispatchTimeMarchesOn(); } From abd19068fba99f4fb1f6e2ddd5146645aafa67ec Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sun, 26 Apr 2026 10:23:39 +0200 Subject: [PATCH 04/12] [image] Hold a strong ref on mInnerImage when requesting a refresh. --- image/ImageWrapper.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/image/ImageWrapper.cpp b/image/ImageWrapper.cpp index c593521c9e..1e3bd3a2f0 100644 --- a/image/ImageWrapper.cpp +++ b/image/ImageWrapper.cpp @@ -252,7 +252,8 @@ ImageWrapper::RequestDiscard() NS_IMETHODIMP_(void) ImageWrapper::RequestRefresh(const TimeStamp& aTime) { - return mInnerImage->RequestRefresh(aTime); + RefPtr inner = mInnerImage; + return inner->RequestRefresh(aTime); } NS_IMETHODIMP From aad6fdd59bcc6ab83fce8c55e98dd3d75c029096 Mon Sep 17 00:00:00 2001 From: John Schanck Date: Sun, 26 Apr 2026 10:27:35 +0200 Subject: [PATCH 05/12] Bug 2029425 - Heap use-after-free in cert_VerifyCertChainOld via dangling certsList[] entry on NameConstraints violation. --- security/nss/lib/certhigh/certvfy.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/security/nss/lib/certhigh/certvfy.c b/security/nss/lib/certhigh/certvfy.c index 4b080a0fb0..23b6a91537 100644 --- a/security/nss/lib/certhigh/certvfy.c +++ b/security/nss/lib/certhigh/certvfy.c @@ -725,7 +725,7 @@ cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert, certsList = tmpCertsList; } for (i = 0; i < subjectNameListLen; i++) { - certsList[namesCount + i] = subjectCert; + certsList[namesCount + i] = CERT_DupCertificate(subjectCert); } namesCount += subjectNameListLen; namesList = cert_CombineNamesLists(namesList, subjectNameList); @@ -972,6 +972,11 @@ loser: rv = SECFailure; done: if (certsList != NULL) { + for (int i = 0; i < namesCount; i++) { + if (certsList[i]) { + CERT_DestroyCertificate(certsList[i]); + } + } PORT_Free(certsList); } if (issuerCert) { From 0329082cf0c038f13b511bc00b6f6f42c1142b63 Mon Sep 17 00:00:00 2001 From: Dennis Jackson Date: Sun, 26 Apr 2026 10:31:29 +0200 Subject: [PATCH 06/12] Bug 2029323 - Improve size calculations in CMS content buffering --- security/nss/lib/smime/cmsdecode.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/security/nss/lib/smime/cmsdecode.c b/security/nss/lib/smime/cmsdecode.c index 69965bdd7d..0edea07d43 100644 --- a/security/nss/lib/smime/cmsdecode.c +++ b/security/nss/lib/smime/cmsdecode.c @@ -529,8 +529,19 @@ nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, SECItem *dataItem = &decoderData->data; offset = dataItem->len; + /* Reject if accumulated size would exceed unsigned int storage. */ + if (len > (unsigned long)(PR_UINT32_MAX - dataItem->len)) { + p7dcx->error = SEC_ERROR_INPUT_LEN; + goto loser; + } if (dataItem->len + len > decoderData->totalBufferSize) { - int needLen = (dataItem->len + len) * 2; + /* Use size_t to avoid truncating the 64-bit sum to int. + * Double to amortize repeated reallocations across chunks. */ + size_t needLen = (size_t)dataItem->len + len; + /* Only double if the result still fits in unsigned int. */ + if (needLen <= PR_UINT32_MAX / 2) { + needLen *= 2; + } dest = (unsigned char *) PORT_ArenaAlloc(p7dcx->cmsg->poolp, needLen); if (dest == NULL) { @@ -541,7 +552,7 @@ nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx, if (dataItem->len) { PORT_Memcpy(dest, dataItem->data, dataItem->len); } - decoderData->totalBufferSize = needLen; + decoderData->totalBufferSize = (unsigned int)needLen; dataItem->data = dest; } From 8a3427845fc5a30f18d273615474b6bdbadd8d8a Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sun, 26 Apr 2026 10:49:29 +0200 Subject: [PATCH 07/12] [js] Use isElement in UnmappedArgSetter to check if the argument was marked as deleted. This is more consistent with `UnmappedArgGetter` and better handles the case where the property was marked deleted in `RareArgumentsData` but not actually deleted from the object due to OOM. Unfortunately this scenario is hard to prevent because the property is marked as deleted in the `ArgumentsObject::obj_delProperty` class hook and after that `NativeDeleteProperty` tries to remove the property and this can fail. --- js/src/vm/ArgumentsObject.cpp | 2 +- js/src/vm/ArgumentsObject.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index 35d5caedc3..a8bc678630 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -690,7 +690,7 @@ UnmappedArgSetter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleVal if (JSID_IS_INT(id)) { unsigned arg = unsigned(JSID_TO_INT(id)); - if (arg < argsobj->initialLength()) { + if (argsobj->isElement(arg)) { argsobj->setElement(cx, arg, vp); return result.succeed(); } diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index 63dc044b0f..59097b710a 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -283,6 +283,20 @@ class ArgumentsObject : public NativeObject bool markElementDeleted(JSContext* cx, uint32_t i); + /* + * Return true if the index is a valid element index for this arguments + * object. + * + * Returning true here doesn't imply that the element value can be read + * through |ArgumentsObject::element()|. For example, unmapped arguments + * objects can have an element index property redefined without having marked + * the element as deleted. Instead, use |maybeGetElement()| or manually check + * for |hasOverriddenElement()|. + */ + bool isElement(uint32_t i) const { + return i < initialLength() && !isElementDeleted(i); + } + /* * An ArgumentsObject serves two roles: * - a real object, accessed through regular object operations, e.g.., From 80b0fe5547c51d51532035d0070ddfbe4f41d7ee Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sun, 26 Apr 2026 11:06:48 +0200 Subject: [PATCH 08/12] [gfx] OTS/HB: Correct bounds checks. --- gfx/ots/src/cmap.cc | 3 ++- gfx/thebes/gfxHarfBuzzShaper.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/gfx/ots/src/cmap.cc b/gfx/ots/src/cmap.cc index 72c2a20fc4..9cfd0039b6 100644 --- a/gfx/ots/src/cmap.cc +++ b/gfx/ots/src/cmap.cc @@ -314,8 +314,9 @@ bool OpenTypeCMAP::Parse31012(const uint8_t *data, size_t length, return Error("format 12 subtable group endCharCode before startCharCode (0x%4X < 0x%4X)", groups[i].end_range, groups[i].start_range); } + // Maximum glyph ID must be less than num_glyphs. if ((groups[i].end_range - groups[i].start_range) + - groups[i].start_glyph_id > num_glyphs) { + groups[i].start_glyph_id >= num_glyphs) { return Error("bad format 12 subtable group startGlyphID (%d)", groups[i].start_glyph_id); } } diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index 7f05f9011f..23dddb604f 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -554,15 +554,17 @@ gfxHarfBuzzShaper::FindGlyf(hb_codepoint_t aGlyph, bool *aEmptyGlyf) const uint32_t len; const char* data = hb_blob_get_data(mLocaTable, &len); if (mLocaLongOffsets) { - if ((aGlyph + 1) * sizeof(AutoSwap_PRUint32) > len) { + // We read offsets[aGlyph] and offsets[aGlyph + 1], so require aGlyph + 2 entries. + if ((aGlyph + 2) * sizeof(AutoSwap_PRUint32) > len) { return nullptr; } const AutoSwap_PRUint32* offsets = reinterpret_cast(data); offset = offsets[aGlyph]; - *aEmptyGlyf = (offset == uint16_t(offsets[aGlyph + 1])); + *aEmptyGlyf = (offset == uint32_t(offsets[aGlyph + 1])); } else { - if ((aGlyph + 1) * sizeof(AutoSwap_PRUint16) > len) { + // Ditto aGlyph + 2 entries. + if ((aGlyph + 2) * sizeof(AutoSwap_PRUint16) > len) { return nullptr; } const AutoSwap_PRUint16* offsets = From 08f1b381fcc7c30c32862bd06758151b0aca72e4 Mon Sep 17 00:00:00 2001 From: Iain Ireland Date: Sun, 26 Apr 2026 11:31:06 +0200 Subject: [PATCH 09/12] Bug 2029727: Update RegExpStatics data after realloc --- js/src/vm/MatchPairs.h | 5 ++++- js/src/vm/RegExpStatics.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/js/src/vm/MatchPairs.h b/js/src/vm/MatchPairs.h index 07c545a7e5..7461ec1cb7 100644 --- a/js/src/vm/MatchPairs.h +++ b/js/src/vm/MatchPairs.h @@ -76,7 +76,10 @@ class MatchPairs virtual bool allocOrExpandArray(size_t pairCount) = 0; bool initArrayFrom(MatchPairs& copyFrom); - void forgetArray() { pairs_ = nullptr; } + void forgetArray() { + pairs_ = nullptr; + pairCount_ = 0; + } void checkAgainst(size_t inputLength) { #ifdef DEBUG diff --git a/js/src/vm/RegExpStatics.h b/js/src/vm/RegExpStatics.h index 686aa7be05..e3d978d4a8 100644 --- a/js/src/vm/RegExpStatics.h +++ b/js/src/vm/RegExpStatics.h @@ -351,6 +351,7 @@ RegExpStatics::updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchP if (!matches.initArrayFrom(newPairs)) { ReportOutOfMemory(cx); + clear(); return false; } From 85e32b82382266a5aaff7a80012c786148c662ce Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sun, 26 Apr 2026 11:48:42 +0200 Subject: [PATCH 10/12] [image] Don't do unnecessary work if the image is being destroyed. --- image/RasterImage.cpp | 9 +++++++-- image/RasterImage.h | 11 +++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index 31a6d7a991..af639f3386 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -94,6 +94,8 @@ RasterImage::RasterImage(ImageURL* aURI /* = nullptr */) : //****************************************************************************** RasterImage::~RasterImage() { + mIsBeingDestroyed = true; + // Make sure our SourceBuffer is marked as complete. This will ensure that any // outstanding decoders terminate. if (!mSourceBuffer->IsComplete()) { @@ -429,8 +431,11 @@ RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) { MOZ_ASSERT(mProgressTracker); - bool animatedFramesDiscarded = - mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated; + if (mIsBeingDestroyed) { + return; + } + + bool animatedFramesDiscarded = aSurfaceKey.Playback() == PlaybackType::eAnimated; RefPtr image = this; NS_DispatchToMainThread(NS_NewRunnableFunction([=]() -> void { diff --git a/image/RasterImage.h b/image/RasterImage.h index e1a7cb1506..64596ff924 100644 --- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -17,6 +17,7 @@ #ifndef mozilla_image_RasterImage_h #define mozilla_image_RasterImage_h +#include "mozilla/Atomics.h" #include "Image.h" #include "nsCOMPtr.h" #include "imgIContainer.h" @@ -384,10 +385,10 @@ private: // data /// If this has a value, we're waiting for SetSize() to send the load event. Maybe mLoadProgress; - nsCOMPtr mProperties; + nsCOMPtr mProperties; /// If this image is animated, a FrameAnimator which manages its animation. - UniquePtr mFrameAnimator; + UniquePtr mFrameAnimator; /// Animation timeline and other state for animation images. Maybe mAnimationState; @@ -400,7 +401,9 @@ private: // data // How many times we've decoded this image. // This is currently only used for statistics - int32_t mDecodeCount; + int32_t mDecodeCount; + + Atomic mIsBeingDestroyed{false}; // A weak pointer to our ImageContainer, which stays alive only as long as // the layer system needs it. @@ -414,7 +417,7 @@ private: // data DrawResult mLastImageContainerDrawResult; #ifdef DEBUG - uint32_t mFramesNotified; + uint32_t mFramesNotified; #endif // The source data for this image. From a7a5092b23c1d7a245db69ab7e38442bcad53b84 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sun, 26 Apr 2026 12:00:21 +0200 Subject: [PATCH 11/12] [media] Spot-fix for ffvpx handling of bad video data. --- media/ffvpx/libavcodec/vp9.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/ffvpx/libavcodec/vp9.c b/media/ffvpx/libavcodec/vp9.c index f2cf194243..e85e461722 100644 --- a/media/ffvpx/libavcodec/vp9.c +++ b/media/ffvpx/libavcodec/vp9.c @@ -207,8 +207,10 @@ static int update_size(AVCodecContext *avctx, int w, int h) *fmtp = AV_PIX_FMT_NONE; ret = ff_thread_get_format(avctx, pix_fmts); - if (ret < 0) + if (ret < 0) { + ff_set_dimensions(avctx, s->w, s->h); return ret; + } avctx->pix_fmt = ret; s->gf_fmt = s->pix_fmt; From 69d0209956c29ea5ec21eaaa6414773389445d6d Mon Sep 17 00:00:00 2001 From: Moonchild Date: Sun, 26 Apr 2026 12:16:13 +0200 Subject: [PATCH 12/12] [parser] Handle foster parenting properly. --- parser/html/nsHtml5TreeOperation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp index 8bd423b901..0803a2b518 100644 --- a/parser/html/nsHtml5TreeOperation.cpp +++ b/parser/html/nsHtml5TreeOperation.cpp @@ -1015,6 +1015,9 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder, nsIContent* table = *(mOne.node); nsIContent* stackParent = *(mTwo.node); nsIContent* fosterParent = GetFosterParent(table, stackParent); + if (fosterParent) { + aBuilder->HoldElement(do_AddRef(fosterParent)); + } *mThree.node = fosterParent; return NS_OK; }