Subject: [PATCH] gnu: librewolf: Fix CVE-2024-9680.

From: Hilton Chain <[email-masked]>
Date: Thu, 10 Oct 2024 23:43:25 +0800

* gnu/packages/patches/librewolf-CVE-2024-9680.patch: New file.
* gnu/local.mk (dist_patch_DATA): Register it.
* gnu/packages/librewolf.scm (librewolf)[source]<patches>: Add it.
(%librewolf-build-id): Update.

Change-Id: Ib32888db1f62eae33f025d034d234a930804f14e
---
 gnu/local.mk                                  |   1 +
 gnu/packages/librewolf.scm                    |   5 +-
 .../patches/librewolf-CVE-2024-9680.patch     | 244 ++++++++++++++++++
 3 files changed, 248 insertions(+), 2 deletions(-)
 create mode 100644 gnu/packages/patches/librewolf-CVE-2024-9680.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index 05a8716063..06e8910162 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1607,6 +1607,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/libgeotiff-fix-tests-on-i386.patch	\
   %D%/packages/patches/libguestfs-syms.patch            	\
   %D%/packages/patches/libobjc2-unbundle-robin-map.patch	\
+  %D%/packages/patches/librewolf-CVE-2024-9680.patch	\
   %D%/packages/patches/libvirt-add-install-prefix.patch	\
   %D%/packages/patches/libziparchive-add-includes.patch		\
   %D%/packages/patches/lightdm-arguments-ordering.patch		\
diff --git a/gnu/packages/librewolf.scm b/gnu/packages/librewolf.scm
index 31de7a7171..f27f49a041 100644
--- a/gnu/packages/librewolf.scm
+++ b/gnu/packages/librewolf.scm
@@ -212,7 +212,7 @@ (define rust-librewolf rust) ; 1.75 is the default in Guix, 1.65 is the minimum.
 ;; Update this id with every update to its release date.
 ;; It's used for cache validation and therefore can lead to strange bugs.
 ;; ex: date '+%Y%m%d%H%M%S'
-(define %librewolf-build-id "20241005085731")
+(define %librewolf-build-id "20241010232659")

 (define-public librewolf
   (package
@@ -223,7 +223,8 @@ (define-public librewolf
       (inherit (make-librewolf-source
                 #:version version
                 #:firefox-hash "0w4z3fq5zhm63a0wmhvmqrj263bvy962dir25q3z0x5hx6hjawh2"
-                #:librewolf-hash "0f80pihn375bdjhjmmg2v1w96wpn76zb60ycy39wafwh1dnzybrd"))))
+                #:librewolf-hash "0f80pihn375bdjhjmmg2v1w96wpn76zb60ycy39wafwh1dnzybrd"))
+      (patches (search-patches "librewolf-CVE-2024-9680.patch"))))
     (build-system gnu-build-system)
     (arguments
      (list
diff --git a/gnu/packages/patches/librewolf-CVE-2024-9680.patch b/gnu/packages/patches/librewolf-CVE-2024-9680.patch
new file mode 100644
index 0000000000..0469823fbc
--- /dev/null
+++ b/gnu/packages/patches/librewolf-CVE-2024-9680.patch
@@ -0,0 +1,244 @@
+Patch fetched from https://hg.mozilla.org/releases/mozilla-release/rev/d2a21d941ed5a73a37b3446caa4a49e74ffe854b
+
+# HG changeset patch
+# User Emilio Cobos Álvarez <[email-masked]>
+# Date 1728404712 0
+# Node ID d2a21d941ed5a73a37b3446caa4a49e74ffe854b
+# Parent  ca8fc4093e5d03c1a9e4482a95409d59cbafb017
+Bug 1923344 - r=smaug, a=dsmith
+
+Differential Revision: https://phabricator.services.mozilla.com/D224958
+
+diff --git a/dom/animation/AnimationTimeline.cpp b/dom/animation/AnimationTimeline.cpp
+--- a/dom/animation/AnimationTimeline.cpp
++++ b/dom/animation/AnimationTimeline.cpp
+@@ -35,71 +35,64 @@ AnimationTimeline::AnimationTimeline(nsI
+   MOZ_ASSERT(mWindow);
+ }
+
+ AnimationTimeline::~AnimationTimeline() { mAnimationOrder.clear(); }
+
+ bool AnimationTimeline::Tick(TickState& aState) {
+   bool needsTicks = false;
+
+-  nsTArray<Animation*> animationsToRemove;
+-
+-  for (Animation* animation = mAnimationOrder.getFirst(); animation;
+-       animation =
+-           static_cast<LinkedListElement<Animation>*>(animation)->getNext()) {
++  AutoTArray<RefPtr<Animation>, 32> animationsToTick;
++  for (Animation* animation : mAnimationOrder) {
+     MOZ_ASSERT(mAnimations.Contains(animation),
+                "The sampling order list should be a subset of the hashset");
+     MOZ_ASSERT(!animation->IsHiddenByContentVisibility(),
+                "The sampling order list should not contain any animations "
+                "that are hidden by content-visibility");
++    animationsToTick.AppendElement(animation);
++  }
+
++  for (Animation* animation : animationsToTick) {
+     // Skip any animations that are longer need associated with this timeline.
+     if (animation->GetTimeline() != this) {
+-      // If animation has some other timeline, it better not be also in the
+-      // animation list of this timeline object!
+-      MOZ_ASSERT(!animation->GetTimeline());
+-      animationsToRemove.AppendElement(animation);
++      RemoveAnimation(animation);
+       continue;
+     }
+
+     needsTicks |= animation->NeedsTicks();
+-    // Even if |animation| doesn't need future ticks, we should still
+-    // Tick it this time around since it might just need a one-off tick in
+-    // order to dispatch events.
++    // Even if |animation| doesn't need future ticks, we should still Tick it
++    // this time around since it might just need a one-off tick in order to
++    // queue events.
+     animation->Tick(aState);
+-
+     if (!animation->NeedsTicks()) {
+-      animationsToRemove.AppendElement(animation);
++      RemoveAnimation(animation);
+     }
+   }
+
+-  for (Animation* animation : animationsToRemove) {
+-    RemoveAnimation(animation);
+-  }
+-
+   return needsTicks;
+ }
+
+ void AnimationTimeline::NotifyAnimationUpdated(Animation& aAnimation) {
+   if (mAnimations.EnsureInserted(&aAnimation)) {
+     if (aAnimation.GetTimeline() && aAnimation.GetTimeline() != this) {
+       aAnimation.GetTimeline()->RemoveAnimation(&aAnimation);
+     }
+     if (!aAnimation.IsHiddenByContentVisibility()) {
+       mAnimationOrder.insertBack(&aAnimation);
+     }
+   }
+ }
+
+ void AnimationTimeline::RemoveAnimation(Animation* aAnimation) {
+-  MOZ_ASSERT(!aAnimation->GetTimeline() || aAnimation->GetTimeline() == this);
+-  if (static_cast<LinkedListElement<Animation>*>(aAnimation)->isInList()) {
++  if (static_cast<LinkedListElement<Animation>*>(aAnimation)->isInList() &&
++      MOZ_LIKELY(!aAnimation->GetTimeline() ||
++                 aAnimation->GetTimeline() == this)) {
++    static_cast<LinkedListElement<Animation>*>(aAnimation)->remove();
+     MOZ_ASSERT(mAnimations.Contains(aAnimation),
+                "The sampling order list should be a subset of the hashset");
+-    static_cast<LinkedListElement<Animation>*>(aAnimation)->remove();
+   }
+   mAnimations.Remove(aAnimation);
+ }
+
+ void AnimationTimeline::NotifyAnimationContentVisibilityChanged(
+     Animation* aAnimation, bool aIsVisible) {
+   bool inList =
+       static_cast<LinkedListElement<Animation>*>(aAnimation)->isInList();
+diff --git a/dom/animation/DocumentTimeline.cpp b/dom/animation/DocumentTimeline.cpp
+--- a/dom/animation/DocumentTimeline.cpp
++++ b/dom/animation/DocumentTimeline.cpp
+@@ -155,17 +155,22 @@ void DocumentTimeline::NotifyAnimationUp
+                  "We should not register with the refresh driver if we are not"
+                  " in the document's list of timelines");
+       refreshDriver->EnsureAnimationUpdate();
+     }
+   }
+ }
+
+ void DocumentTimeline::TriggerAllPendingAnimationsNow() {
++  AutoTArray<RefPtr<Animation>, 32> animationsToTrigger;
+   for (Animation* animation : mAnimationOrder) {
++    animationsToTrigger.AppendElement(animation);
++  }
++
++  for (Animation* animation : animationsToTrigger) {
+     animation->TryTriggerNow();
+   }
+ }
+
+ void DocumentTimeline::WillRefresh() {
+   if (!mDocument->GetPresShell()) {
+     // If we're not displayed, don't tick animations.
+     return;
+@@ -183,19 +188,16 @@ void DocumentTimeline::WillRefresh() {
+   }
+   // We already assert that GetRefreshDriver() is non-null at the beginning
+   // of this function but we check it again here to be sure that ticking
+   // animations does not have any side effects that cause us to lose the
+   // connection with the refresh driver, such as triggering the destruction
+   // of mDocument's PresShell.
+   if (nsRefreshDriver* refreshDriver = GetRefreshDriver()) {
+     refreshDriver->EnsureAnimationUpdate();
+-  } else {
+-    MOZ_ASSERT_UNREACHABLE(
+-        "Refresh driver should still be valid at end of WillRefresh");
+   }
+ }
+
+ void DocumentTimeline::RemoveAnimation(Animation* aAnimation) {
+   AnimationTimeline::RemoveAnimation(aAnimation);
+ }
+
+ void DocumentTimeline::NotifyAnimationContentVisibilityChanged(
+diff --git a/dom/animation/ScrollTimelineAnimationTracker.cpp b/dom/animation/ScrollTimelineAnimationTracker.cpp
+--- a/dom/animation/ScrollTimelineAnimationTracker.cpp
++++ b/dom/animation/ScrollTimelineAnimationTracker.cpp
+@@ -8,23 +8,20 @@
+
+ #include "mozilla/dom/Document.h"
+
+ namespace mozilla {
+
+ NS_IMPL_CYCLE_COLLECTION(ScrollTimelineAnimationTracker, mPendingSet, mDocument)
+
+ void ScrollTimelineAnimationTracker::TriggerPendingAnimations() {
+-  for (auto iter = mPendingSet.begin(), end = mPendingSet.end(); iter != end;
+-       ++iter) {
+-    dom::Animation* animation = *iter;
+-
++  for (RefPtr<dom::Animation>& animation :
++       ToTArray<AutoTArray<RefPtr<dom::Animation>, 32>>(mPendingSet)) {
+     MOZ_ASSERT(animation->GetTimeline() &&
+                !animation->GetTimeline()->IsMonotonicallyIncreasing());
+-
+     // FIXME: Trigger now may not be correct because the spec says:
+     // If a user agent determines that animation is immediately ready, it may
+     // schedule the task (i.e. ResumeAt()) as a microtask such that it runs at
+     // the next microtask checkpoint, but it must not perform the task
+     // synchronously.
+     // Note: So, for now, we put the animation into the tracker, and trigger
+     // them immediately until the frames are ready. Using TriggerOnNextTick()
+     // for scroll-driven animations may have issues because we don't tick if
+@@ -34,15 +31,13 @@ void ScrollTimelineAnimationTracker::Tri
+       // inactive. It's pretty hard to tell its future status, for example, it's
+       // possible that the scroll container is in display:none subtree but the
+       // animating element isn't the subtree, then we need to keep tracking the
+       // situation until the scroll container gets framed. so in general we make
+       // this animation be pending (i.e. not ready) if its scroll-timeline is
+       // inactive, and this also matches the current spec definition.
+       continue;
+     }
+-
+-    // Note: Remove() is legitimately called once per entry during the loop.
+-    mPendingSet.Remove(iter);
++    mPendingSet.Remove(animation);
+   }
+ }
+
+ }  // namespace mozilla
+diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp
+--- a/layout/base/nsRefreshDriver.cpp
++++ b/layout/base/nsRefreshDriver.cpp
+@@ -2290,18 +2290,25 @@ void nsRefreshDriver::DetermineProximity
+                                                        ShouldCollect);
+
+   for (const RefPtr<Document>& doc : documents) {
+     MOZ_KnownLive(doc)->DetermineProximityToViewportAndNotifyResizeObservers();
+   }
+ }
+
+ static CallState UpdateAndReduceAnimations(Document& aDocument) {
+-  for (DocumentTimeline* timeline : aDocument.Timelines()) {
+-    timeline->WillRefresh();
++  {
++    AutoTArray<RefPtr<DocumentTimeline>, 32> timelinesToTick;
++    for (DocumentTimeline* timeline : aDocument.Timelines()) {
++      timelinesToTick.AppendElement(timeline);
++    }
++
++    for (DocumentTimeline* tl : timelinesToTick) {
++      tl->WillRefresh();
++    }
+   }
+
+   if (nsPresContext* pc = aDocument.GetPresContext()) {
+     if (pc->EffectCompositor()->NeedsReducing()) {
+       pc->EffectCompositor()->ReduceAnimations();
+     }
+   }
+   aDocument.EnumerateSubDocuments(UpdateAndReduceAnimations);
+@@ -2321,17 +2328,18 @@ void nsRefreshDriver::UpdateAnimationsAn
+     // run these, however, until we have fully updated the animation state. As
+     // per the "update animations and send events" procedure[1], we should
+     // remove replaced animations and then run these microtasks before
+     // dispatching the corresponding animation events.
+     //
+     // [1]:
+     // https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
+     nsAutoMicroTask mt;
+-    UpdateAndReduceAnimations(*mPresContext->Document());
++    RefPtr doc = mPresContext->Document();
++    UpdateAndReduceAnimations(*doc);
+   }
+
+   // Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
+   // a RefPtr<> array since each AnimationEventDispatcher might be destroyed
+   // during processing the previous dispatcher.
+   AutoTArray<RefPtr<AnimationEventDispatcher>, 16> dispatchers;
+   dispatchers.AppendElements(mAnimationEventFlushObservers);
+   mAnimationEventFlushObservers.Clear();
+

base-commit: 39df1ac928beeeb316980c5ecb5dfaae7053460d
--
2.46.0