Browse Source

chore: backport security fixes from Chromium release M90-3 (#29250)

* Backports of security fixes in Chromium release M90-3
https://chromereleases.googleblog.com/2021/05/stable-channel-update-for-desktop.html

Includes backports for the following Chromium issues:
https://crbug.com/1204071
https://crbug.com/1203590 (CVE-2021-30518)
https://crbug.com/1201446 (CVE-2021-30516)
https://crbug.com/1201073 (CVE-2021-30515)
https://crbug.com/1200490 (CVE-2021-30513)
https://crbug.com/1200019 (CVE-2021-30512)
https://crbug.com/1197436 (CVE-2021-30510)
https://crbug.com/1195340 (CVE-2021-30508)
https://crbug.com/1180126
https://crbug.com/1203667
https://crbug.com/1201340

* chore: update patches

* fix patch for 1200490

* chore: update patches

Co-authored-by: Andrey Belenko <[email protected]>
Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com>
Andrey Belenko 3 years ago
parent
commit
acc11230fd

+ 8 - 0
patches/chromium/.patches

@@ -159,3 +159,11 @@ cherry-pick-406ae3e8a9a8.patch
 cherry-pick-668cf831e912.patch
 cherry-pick-02f5ef8c88d7.patch
 cherry-pick-f37149c4434f.patch
+replace_std_vector_with_base_observerlist_to_support_container.patch
+guard_webcontents_downloadimage_against_malformed_renderer.patch
+fileapi_terminate_filereaderloader_before_dispatching_onabort_event.patch
+notifications_crash_if_improper_action_icons_sent_from_renderer.patch
+reland_views_handle_deletion_when_toggling_fullscreen.patch
+media_feeds_disable_media_feeds_and_related_features.patch
+remove_tabs_and_line_breaks_from_the_middle_of_app_names_when.patch
+autofill_fixed_refill_of_changed_form.patch

+ 41 - 0
patches/chromium/autofill_fixed_refill_of_changed_form.patch

@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Wed, 19 May 2021 17:22:52 +0200
+Subject: Fixed refill of changed form.
+
+(cherry picked from commit 533bb3adcfe3499f90e2646fc60312f303b963ac)
+
+Bug: 1203667
+Change-Id: I2693a024531775e0e60cc330107d77d10558f466
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2867655
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2874611
+
+diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
+index 0b72ac3b8a0d3bb3294be995d2b9a4bc52b81a8f..5f92b20734d06249fc119432f45def087214709c 100644
+--- a/components/autofill/core/browser/autofill_manager.cc
++++ b/components/autofill/core/browser/autofill_manager.cc
+@@ -1743,7 +1743,10 @@ void AutofillManager::FillOrPreviewDataModelForm(
+   form_structure->RationalizePhoneNumbersInSection(autofill_field->section);
+ 
+   FormData result = form;
+-  DCHECK_EQ(form_structure->field_count(), form.fields.size());
++  // TODO(crbug/1203667#c9): Skip if the form has changed in the meantime, which
++  // may happen with refills.
++  if (form_structure->field_count() != form.fields.size())
++    return;
+ 
+   if (action == AutofillDriver::FORM_DATA_ACTION_FILL && !is_refill) {
+     filling_contexts_map_[form_structure->GetIdentifierForRefill()] =
+@@ -1787,8 +1790,10 @@ void AutofillManager::FillOrPreviewDataModelForm(
+       continue;
+     }
+ 
+-    // The field order should be the same in |form_structure| and |result|.
+-    DCHECK(form_structure->field(i)->SameFieldAs(result.fields[i]));
++    // TODO(crbug/1203667#c9): Skip if the form has changed in the meantime,
++    // which may happen with refills.
++    if (!form_structure->field(i)->SameFieldAs(result.fields[i]))
++      continue;
+ 
+     AutofillField* cached_field = form_structure->field(i);
+     FieldTypeGroup field_group_type = cached_field->Type().group();

+ 58 - 0
patches/chromium/fileapi_terminate_filereaderloader_before_dispatching_onabort_event.patch

@@ -0,0 +1,58 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Wed, 19 May 2021 12:27:18 +0200
+Subject: FileAPI: Terminate FileReaderLoader before dispatching onabort event.
+
+Otherwise FileReader could end up in an inconsistent state where a load
+is still in progress while the state was set to done.
+
+(cherry picked from commit a74c980df61dd7367ad1b11e6a735be82d2696f0)
+
+Bug: 1201073
+Change-Id: Ib2c833537e1badc57d125568d5d35f53f12582a8
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2860442
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2871355
+
+diff --git a/third_party/blink/renderer/core/fileapi/file_reader.cc b/third_party/blink/renderer/core/fileapi/file_reader.cc
+index 2bbc4db7923c659eda6d63230998ca3414f397d3..93221ba5cf92b9efd702378e1b94cca4fb6ab470 100644
+--- a/third_party/blink/renderer/core/fileapi/file_reader.cc
++++ b/third_party/blink/renderer/core/fileapi/file_reader.cc
+@@ -335,7 +335,10 @@ void FileReader::abort() {
+   loading_state_ = kLoadingStateAborted;
+ 
+   DCHECK_NE(kDone, state_);
+-  state_ = kDone;
++  // Synchronously cancel the loader before dispatching events. This way we make
++  // sure the FileReader internal state stays consistent even if another load
++  // is started from one of the event handlers, or right after abort returns.
++  Terminate();
+ 
+   base::AutoReset<bool> firing_events(&still_firing_events_, true);
+ 
+@@ -347,15 +350,12 @@ void FileReader::abort() {
+       ThrottlingController::RemoveReader(GetExecutionContext(), this);
+ 
+   FireEvent(event_type_names::kAbort);
++  // TODO(https://crbug.com/1204139): Only fire loadend event if no new load was
++  // started from the abort event handler.
+   FireEvent(event_type_names::kLoadend);
+ 
+   // All possible events have fired and we're done, no more pending activity.
+   ThrottlingController::FinishReader(GetExecutionContext(), this, final_step);
+-
+-  // Also synchronously cancel the loader, as script might initiate a new load
+-  // right after this method returns, in which case an async termination would
+-  // terminate the wrong loader.
+-  Terminate();
+ }
+ 
+ void FileReader::result(StringOrArrayBuffer& result_attribute) const {
+@@ -428,6 +428,8 @@ void FileReader::DidFinishLoading() {
+       ThrottlingController::RemoveReader(GetExecutionContext(), this);
+ 
+   FireEvent(event_type_names::kLoad);
++  // TODO(https://crbug.com/1204139): Only fire loadend event if no new load was
++  // started from the abort event handler.
+   FireEvent(event_type_names::kLoadend);
+ 
+   // All possible events have fired and we're done, no more pending activity.

+ 232 - 0
patches/chromium/guard_webcontents_downloadimage_against_malformed_renderer.patch

@@ -0,0 +1,232 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Wed, 19 May 2021 10:54:46 +0200
+Subject: Guard WebContents::DownloadImage() against malformed renderer
+ response
+
+Callers expect that ImageDownloadCallback gets invoked with two vectors
+having the same number of elements (one containing the bitmaps and the
+other one the corresponding sizes).
+
+However, these vectors are populated directly from the Mojo response,
+so there needs to be some browser-process sanitization to protect
+against buggy or compromised renderers.
+
+In this patch, WebContentsImpl::OnDidDownloadImage() mimics a 400 error
+if the response is malformed, similarly to how it's done in other edge
+cases (renderer process dead upon download). Because this scenario is
+a violation of the Mojo API contract, the browser process also issues
+a bad message log (newly-introduced WCI_INVALID_DOWNLOAD_IMAGE_RESULT)
+and shuts down the renderer process.
+
+(cherry picked from commit 034ba14e44f08e8ca84b42350f3238f847e08e5f)
+
+Change-Id: Ic0843e10efc26809fabd8f1bbe506ba1703d1486
+Fixed: 1201446
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2871796
+
+diff --git a/components/favicon/core/favicon_handler.cc b/components/favicon/core/favicon_handler.cc
+index 3d278c8e7cb1212c266e3b1838be8f658165a795..197901e64c7cd8d8e2750c28d502ee5428b35df4 100644
+--- a/components/favicon/core/favicon_handler.cc
++++ b/components/favicon/core/favicon_handler.cc
+@@ -507,6 +507,8 @@ void FaviconHandler::OnDidDownloadFavicon(
+     const GURL& image_url,
+     const std::vector<SkBitmap>& bitmaps,
+     const std::vector<gfx::Size>& original_bitmap_sizes) {
++  DCHECK_EQ(bitmaps.size(), original_bitmap_sizes.size());
++
+   // Mark download as finished.
+   image_download_request_.Cancel();
+ 
+diff --git a/components/favicon/core/favicon_handler.h b/components/favicon/core/favicon_handler.h
+index 98706de7a7bcdeee857acd5a9113c45ecf82dca7..574d88ac91a6322644cbe5fda9e964807830c20e 100644
+--- a/components/favicon/core/favicon_handler.h
++++ b/components/favicon/core/favicon_handler.h
+@@ -238,7 +238,9 @@ class FaviconHandler {
+   void ScheduleImageDownload(const GURL& image_url,
+                              favicon_base::IconType icon_type);
+ 
+-  // Triggered when a download of an image has finished.
++  // Triggered when a download of an image has finished. |bitmaps| and
++  // |original_bitmap_sizes| must contain the same number of elements (i.e. same
++  // vector size).
+   void OnDidDownloadFavicon(
+       favicon_base::IconType icon_type,
+       int id,
+diff --git a/components/favicon/ios/web_favicon_driver.mm b/components/favicon/ios/web_favicon_driver.mm
+index 963a4dbcc5c971ea53c5d603cc73d1d2dab4f3ae..1dd5f4f2b38b55b8831aa527dd52ae11d45632fb 100644
+--- a/components/favicon/ios/web_favicon_driver.mm
++++ b/components/favicon/ios/web_favicon_driver.mm
+@@ -75,6 +75,7 @@
+           for (const auto& frame : frames) {
+             sizes.push_back(gfx::Size(frame.width(), frame.height()));
+           }
++          DCHECK_EQ(frames.size(), sizes.size());
+         }
+         std::move(local_callback)
+             .Run(local_download_id, metadata.http_response_code, local_url,
+diff --git a/components/favicon_base/select_favicon_frames.cc b/components/favicon_base/select_favicon_frames.cc
+index 90b58e621492d0e503f7965de91581c0a451d163..73cd7dd5331a818f413d831b4ae596bc88805a8a 100644
+--- a/components/favicon_base/select_favicon_frames.cc
++++ b/components/favicon_base/select_favicon_frames.cc
+@@ -216,6 +216,7 @@ gfx::ImageSkia CreateFaviconImageSkia(
+     const std::vector<gfx::Size>& original_sizes,
+     int desired_size_in_dip,
+     float* score) {
++  DCHECK_EQ(bitmaps.size(), original_sizes.size());
+ 
+   const std::vector<float>& favicon_scales = favicon_base::GetFaviconScales();
+   std::vector<int> desired_sizes;
+diff --git a/components/favicon_base/select_favicon_frames.h b/components/favicon_base/select_favicon_frames.h
+index 573a38c79cddf839622488589b71b4d19fbdfba6..eab1e54466763e5a6a6069a29e877da054972fa8 100644
+--- a/components/favicon_base/select_favicon_frames.h
++++ b/components/favicon_base/select_favicon_frames.h
+@@ -38,6 +38,8 @@ extern const float kSelectFaviconFramesInvalidScore;
+ // it inspired by this method.
+ // If an unsupported scale (not in the favicon_base::GetFaviconScales())
+ // is requested, the ImageSkia will automatically scales using lancoz3.
++// |original_sizes| represents the pixel sizes of the favicon bitmaps in
++// |bitmaps|, which also means both vectors must have the same size.
+ gfx::ImageSkia CreateFaviconImageSkia(
+     const std::vector<SkBitmap>& bitmaps,
+     const std::vector<gfx::Size>& original_sizes,
+diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
+index c6675ef501de6cbaa175793ac0b13ba9b93a12e6..e198d661c711dc648a4e4c2249e0a60f69c406da 100644
+--- a/content/browser/bad_message.h
++++ b/content/browser/bad_message.h
+@@ -260,6 +260,15 @@ enum BadMessageReason {
+   RFH_RECEIVED_ASSOCIATED_MESSAGE_WHILE_BFCACHED = 232,
+   RWH_CLOSE_PORTAL = 233,
+   MSDH_INVALID_STREAM_TYPE = 234,
++  RFH_CREATE_CHILD_FRAME_TOKENS_NOT_FOUND = 235,
++  ASGH_ASSOCIATED_INTERFACE_REQUEST = 236,
++  ASGH_RECEIVED_CONTROL_MESSAGE = 237,
++  CSDH_BAD_OWNER = 238,
++  SYNC_COMPOSITOR_NO_LOCAL_SURFACE_ID = 239,
++  WCI_INVALID_FULLSCREEN_OPTIONS = 240,
++  PAYMENTS_WITHOUT_PERMISSION = 241,
++  WEB_BUNDLE_INVALID_NAVIGATION_URL = 242,
++  WCI_INVALID_DOWNLOAD_IMAGE_RESULT = 243,
+ 
+   // Please add new elements here. The naming convention is abbreviated class
+   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
+diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
+index 750944d45d66f70a5e32219148a4f0cb9edd351c..d0e25934cda73577524b80051b7e4cd6150077e8 100644
+--- a/content/browser/web_contents/web_contents_impl.cc
++++ b/content/browser/web_contents/web_contents_impl.cc
+@@ -169,6 +169,7 @@
+ #include "third_party/blink/public/common/web_preferences/web_preferences.h"
+ #include "third_party/blink/public/mojom/frame/frame.mojom.h"
+ #include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
++#include "third_party/blink/public/mojom/image_downloader/image_downloader.mojom.h"
+ #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom.h"
+ #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
+ #include "third_party/skia/include/core/SkBitmap.h"
+@@ -5066,18 +5067,18 @@ int WebContentsImpl::DownloadImageInFrame(
+     // respond with a 400 HTTP error code to indicate that something went wrong.
+     GetUIThreadTaskRunner({})->PostTask(
+         FROM_HERE,
+-        base::BindOnce(&WebContentsImpl::OnDidDownloadImage,
+-                       weak_factory_.GetWeakPtr(), std::move(callback),
+-                       download_id, url, 400, std::vector<SkBitmap>(),
+-                       std::vector<gfx::Size>()));
++        base::BindOnce(
++            &WebContentsImpl::OnDidDownloadImage, weak_factory_.GetWeakPtr(),
++            initiator_frame->GetWeakPtr(), std::move(callback), download_id,
++            url, 400, std::vector<SkBitmap>(), std::vector<gfx::Size>()));
+     return download_id;
+   }
+ 
+   mojo_image_downloader->DownloadImage(
+       url, is_favicon, preferred_size, max_bitmap_size, bypass_cache,
+       base::BindOnce(&WebContentsImpl::OnDidDownloadImage,
+-                     weak_factory_.GetWeakPtr(), std::move(callback),
+-                     download_id, url));
++                     weak_factory_.GetWeakPtr(), initiator_frame->GetWeakPtr(),
++                     std::move(callback), download_id, url));
+   return download_id;
+ }
+ 
+@@ -8044,6 +8045,7 @@ bool WebContentsImpl::CompletedFirstVisuallyNonEmptyPaint() {
+ }
+ 
+ void WebContentsImpl::OnDidDownloadImage(
++    base::WeakPtr<RenderFrameHostImpl> rfh,
+     ImageDownloadCallback callback,
+     int id,
+     const GURL& image_url,
+@@ -8053,6 +8055,21 @@ void WebContentsImpl::OnDidDownloadImage(
+   OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnDidDownloadImage",
+                         "image_url",
+                         base::trace_event::ValueToString(image_url));
++
++  // Guard against buggy or compromised renderers that could violate the API
++  // contract that |images| and |original_image_sizes| must have the same
++  // length.
++  if (images.size() != original_image_sizes.size()) {
++    if (rfh) {
++      ReceivedBadMessage(rfh->GetProcess(),
++                         bad_message::WCI_INVALID_DOWNLOAD_IMAGE_RESULT);
++    }
++    // Respond with a 400 to indicate that something went wrong.
++    std::move(callback).Run(id, 400, image_url, std::vector<SkBitmap>(),
++                            std::vector<gfx::Size>());
++    return;
++  }
++
+   std::move(callback).Run(id, http_status_code, image_url, images,
+                           original_image_sizes);
+ }
+diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
+index b78cb8f558052e01d61456ca282359eb4b342c35..54a780a2cbbe1259b13d5d46c9a90aaffbb516a9 100644
+--- a/content/browser/web_contents/web_contents_impl.h
++++ b/content/browser/web_contents/web_contents_impl.h
+@@ -1470,7 +1470,8 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
+       std::set<RenderWidgetHostView*>& result);
+ 
+   // Called with the result of a DownloadImage() request.
+-  void OnDidDownloadImage(ImageDownloadCallback callback,
++  void OnDidDownloadImage(base::WeakPtr<RenderFrameHostImpl> rfh,
++                          ImageDownloadCallback callback,
+                           int id,
+                           const GURL& image_url,
+                           int32_t http_status_code,
+diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
+index a43c9972377f6365c3fe0e33f0d29700f597bd2d..8260da280d387847e18ca22782e71c0d6614385f 100644
+--- a/content/public/browser/web_contents.h
++++ b/content/public/browser/web_contents.h
+@@ -918,8 +918,9 @@ class WebContents : public PageNavigator,
+   // |bitmaps| will be empty on download failure.
+   // |sizes| are the sizes in pixels of the bitmaps before they were resized due
+   // to the max bitmap size passed to DownloadImage(). Each entry in the bitmaps
+-  // vector corresponds to an entry in the sizes vector. If a bitmap was
+-  // resized, there should be a single returned bitmap.
++  // vector corresponds to an entry in the sizes vector (both vector sizes are
++  // guaranteed to be equal). If a bitmap was resized, there should be a single
++  // returned bitmap.
+   using ImageDownloadCallback =
+       base::OnceCallback<void(int id,
+                               int http_status_code,
+diff --git a/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.cc b/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.cc
+index fb0fc97d689c8c6b1f6f9fa27d250e0438550725..740c6c01318a1d9d2914ddef9949db9ced559d14 100644
+--- a/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.cc
++++ b/third_party/blink/renderer/modules/image_downloader/image_downloader_impl.cc
+@@ -79,7 +79,8 @@ SkBitmap ResizeImage(const SkBitmap& image, uint32_t max_image_size) {
+ // size |max_image_size|. Returns the result if it is not empty. Otherwise,
+ // find the smallest image in the array and resize it proportionally to fit
+ // in a box of size |max_image_size|.
+-// Sets |original_image_sizes| to the sizes of |images| before resizing.
++// Sets |original_image_sizes| to the sizes of |images| before resizing. Both
++// output vectors are guaranteed to have the same size.
+ void FilterAndResizeImagesForMaximalSize(
+     const WTF::Vector<SkBitmap>& unfiltered,
+     uint32_t max_image_size,
+@@ -202,6 +203,8 @@ void ImageDownloaderImpl::DidDownloadImage(
+   FilterAndResizeImagesForMaximalSize(images, max_image_size, &result_images,
+                                       &result_original_image_sizes);
+ 
++  DCHECK_EQ(result_images.size(), result_original_image_sizes.size());
++
+   std::move(callback).Run(http_status_code, result_images,
+                           result_original_image_sizes);
+ }

+ 41 - 0
patches/chromium/media_feeds_disable_media_feeds_and_related_features.patch

@@ -0,0 +1,41 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Wed, 19 May 2021 14:12:59 +0200
+Subject: Media Feeds: Disable Media Feeds and related features
+
+Media Feeds is deleted in M91 and later and is unused in previous
+versions as well. There is a security issue with Media Feeds though, so
+we'd like to force it to be disabled in previous versions, so this CL
+turns it off for M90.
+
+(cherry picked from commit b064a73431541e520d273c227e762983c2f177b7)
+
+Bug: 1195340
+Change-Id: I29e18be2abe4c1b4560d6324af3b6da93a97d947
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2847504
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2883741
+
+diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
+index 0c20d23e273405a78f59d91196f12160dba2e824..37fc954bffa26cad04a8d31e2838416f84670281 100644
+--- a/media/base/media_switches.cc
++++ b/media/base/media_switches.cc
+@@ -730,15 +730,16 @@ const base::Feature kMediaEngagementHTTPSOnly{
+ 
+ // Enables Media Feeds to allow sites to provide specific recommendations for
+ // users.
+-const base::Feature kMediaFeeds{"MediaFeeds", base::FEATURE_ENABLED_BY_DEFAULT};
++const base::Feature kMediaFeeds{"MediaFeeds",
++                                base::FEATURE_DISABLED_BY_DEFAULT};
+ 
+ // Enables fetching Media Feeds periodically in the background.
+ const base::Feature kMediaFeedsBackgroundFetching{
+-    "MediaFeedsBackgroundFetching", base::FEATURE_ENABLED_BY_DEFAULT};
++    "MediaFeedsBackgroundFetching", base::FEATURE_DISABLED_BY_DEFAULT};
+ 
+ // Enables checking Media Feeds against safe search to prevent adult content.
+ const base::Feature kMediaFeedsSafeSearch{"MediaFeedsSafeSearch",
+-                                          base::FEATURE_ENABLED_BY_DEFAULT};
++                                          base::FEATURE_DISABLED_BY_DEFAULT};
+ 
+ // Send events to devtools rather than to chrome://media-internals
+ const base::Feature kMediaInspectorLogging{"MediaInspectorLogging",

+ 149 - 0
patches/chromium/notifications_crash_if_improper_action_icons_sent_from_renderer.patch

@@ -0,0 +1,149 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Wed, 19 May 2021 12:54:32 +0200
+Subject: Notifications: crash if improper action icons sent from renderer.
+
+Previously, the code only called DCHECK but as this data is from a
+renderer we should probably crash the browser.
+
+(cherry picked from commit 3b28dc50187b22e080ad9c1e4e6c4f3b08f3136d)
+
+Bug: 1200019
+Change-Id: If4d9d48c8e18a3ed9c8bb3a50b952591259e0db5
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2838205
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2872493
+
+diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc
+index 403cf8f11bec68504e32ebace2f80e04ad136491..5d5f7fafd484ff5cb9fe31b459b5b23b740cf513 100644
+--- a/chrome/browser/notifications/platform_notification_service_impl.cc
++++ b/chrome/browser/notifications/platform_notification_service_impl.cc
+@@ -403,8 +403,10 @@ PlatformNotificationServiceImpl::CreateNotificationFromData(
+     const std::string& notification_id,
+     const blink::PlatformNotificationData& notification_data,
+     const blink::NotificationResources& notification_resources) const {
+-  DCHECK_EQ(notification_data.actions.size(),
+-            notification_resources.action_icons.size());
++  // Blink always populates action icons to match the actions, even if no icon
++  // was fetched, so this indicates a compromised renderer.
++  CHECK_EQ(notification_data.actions.size(),
++           notification_resources.action_icons.size());
+ 
+   message_center::RichNotificationData optional_fields;
+ 
+diff --git a/content/browser/notifications/blink_notification_service_impl.cc b/content/browser/notifications/blink_notification_service_impl.cc
+index 7105781e0019b456f287fd0ebb6e309efe2cecad..8fcbb96ed39da9ec468cbe310bf8f499e338a235 100644
+--- a/content/browser/notifications/blink_notification_service_impl.cc
++++ b/content/browser/notifications/blink_notification_service_impl.cc
+@@ -39,6 +39,9 @@ const char kBadMessageImproperNotificationImage[] =
+     "disabled.";
+ const char kBadMessageInvalidNotificationTriggerTimestamp[] =
+     "Received an invalid notification trigger timestamp.";
++const char kBadMessageInvalidNotificationActionButtons[] =
++    "Received a notification with a number of action images that does not "
++    "match the number of actions.";
+ 
+ // Returns the implementation of the PlatformNotificationService. May be NULL.
+ PlatformNotificationService* GetNotificationService(
+@@ -134,7 +137,8 @@ void BlinkNotificationServiceImpl::DisplayNonPersistentNotification(
+     mojo::PendingRemote<blink::mojom::NonPersistentNotificationListener>
+         event_listener_remote) {
+   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+-  if (!ValidateNotificationResources(notification_resources))
++  if (!ValidateNotificationDataAndResources(platform_notification_data,
++                                            notification_resources))
+     return;
+ 
+   if (!GetNotificationService(browser_context_))
+@@ -190,28 +194,32 @@ BlinkNotificationServiceImpl::CheckPermissionStatus() {
+                             origin_.GetURL());
+ }
+ 
+-bool BlinkNotificationServiceImpl::ValidateNotificationResources(
++bool BlinkNotificationServiceImpl::ValidateNotificationDataAndResources(
++    const blink::PlatformNotificationData& platform_notification_data,
+     const blink::NotificationResources& notification_resources) {
+-  if (notification_resources.image.drawsNothing() ||
+-      base::FeatureList::IsEnabled(features::kNotificationContentImage))
+-    return true;
+-  receiver_.ReportBadMessage(kBadMessageImproperNotificationImage);
+-  // The above ReportBadMessage() closes |binding_| but does not trigger its
+-  // connection error handler, so we need to call the error handler explicitly
+-  // here to do some necessary work.
+-  OnConnectionError();
+-  return false;
+-}
++  if (platform_notification_data.actions.size() !=
++      notification_resources.action_icons.size()) {
++    receiver_.ReportBadMessage(kBadMessageInvalidNotificationActionButtons);
++    OnConnectionError();
++    return false;
++  }
+ 
+-// Checks if this notification has a valid trigger.
+-bool BlinkNotificationServiceImpl::ValidateNotificationData(
+-    const blink::PlatformNotificationData& notification_data) {
+-  if (!CheckNotificationTriggerRange(notification_data)) {
++  if (!CheckNotificationTriggerRange(platform_notification_data)) {
+     receiver_.ReportBadMessage(kBadMessageInvalidNotificationTriggerTimestamp);
+     OnConnectionError();
+     return false;
+   }
+ 
++  if (!notification_resources.image.drawsNothing() &&
++      !base::FeatureList::IsEnabled(features::kNotificationContentImage)) {
++    receiver_.ReportBadMessage(kBadMessageImproperNotificationImage);
++    // The above ReportBadMessage() closes |binding_| but does not trigger its
++    // connection error handler, so we need to call the error handler explicitly
++    // here to do some necessary work.
++    OnConnectionError();
++    return false;
++  }
++
+   return true;
+ }
+ 
+@@ -221,10 +229,8 @@ void BlinkNotificationServiceImpl::DisplayPersistentNotification(
+     const blink::NotificationResources& notification_resources,
+     DisplayPersistentNotificationCallback callback) {
+   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+-  if (!ValidateNotificationResources(notification_resources))
+-    return;
+-
+-  if (!ValidateNotificationData(platform_notification_data))
++  if (!ValidateNotificationDataAndResources(platform_notification_data,
++                                            notification_resources))
+     return;
+ 
+   if (!GetNotificationService(browser_context_)) {
+diff --git a/content/browser/notifications/blink_notification_service_impl.h b/content/browser/notifications/blink_notification_service_impl.h
+index dc5307e6500b0bfb5da83e8d8ff8886b91133522..fe1abadd2bc196914cb7b6d9fe29a75435f08988 100644
+--- a/content/browser/notifications/blink_notification_service_impl.h
++++ b/content/browser/notifications/blink_notification_service_impl.h
+@@ -72,20 +72,15 @@ class CONTENT_EXPORT BlinkNotificationServiceImpl
+   // Check the permission status for the current |origin_|.
+   blink::mojom::PermissionStatus CheckPermissionStatus();
+ 
+-  // Validate |notification_resources| received in a Mojo IPC message.
+-  // If the validation failed, we'd close the Mojo connection |binding_| and
+-  // destroy |this| by calling OnConnectionError() directly, then return false.
+-  // So, please do not touch |this| again after you got a false return value.
+-  bool ValidateNotificationResources(
++  // Validate |notification_data| and |notification_resources| received in a
++  // Mojo IPC message. If the validation failed, we'd close the Mojo connection
++  // |binding_| and destroy |this| by calling OnConnectionError() directly, then
++  // return false. So, please do not touch |this| again after you got a false
++  // return value.
++  bool ValidateNotificationDataAndResources(
++      const blink::PlatformNotificationData& notification_data,
+       const blink::NotificationResources& notification_resources);
+ 
+-  // Validate |notification_data| received in a Mojo IPC message.
+-  // If the validation failed, we'd close the Mojo connection |binding_| and
+-  // destroy |this| by calling OnConnectionError() directly, then return false.
+-  // So, please do not touch |this| again after you got a false return value.
+-  bool ValidateNotificationData(
+-      const blink::PlatformNotificationData& notification_data);
+-
+   void DidWriteNotificationData(DisplayPersistentNotificationCallback callback,
+                                 bool success,
+                                 const std::string& notification_id);

+ 149 - 0
patches/chromium/reland_views_handle_deletion_when_toggling_fullscreen.patch

@@ -0,0 +1,149 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Wed, 19 May 2021 13:18:02 +0200
+Subject: views: handle deletion when toggling fullscreen
+
+This differs from the first in so far as needing to add more early
+outs in the windows side if destroyed. This was caught by the asan
+bot.
+
+Toggling fullscreen means the bounds change. There are some
+code paths that may delete the Widget when the bounds changes.
+This patch ensures the right thing happens if the Widget is
+deleted when this happens.
+
+BUG=1197436
+
+(cherry picked from commit 60fe7a686c0620855c28a60721f668a99e409ee4)
+
+Change-Id: I8ce8f2045878b6f6de530f58e386149189900498
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2857227
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2868317
+
+diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+index 6a1fcf7fe75d8fadbcb024c7434651bfaf05dc73..e539f14cb2bd56000dccfc557ca6573a3b53dcf1 100644
+--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
++++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+@@ -583,7 +583,10 @@ void DesktopWindowTreeHostPlatform::SetFullscreen(bool fullscreen) {
+   if (IsFullscreen() == fullscreen)
+     return;
+ 
++  auto weak_ptr = GetWeakPtr();
+   platform_window()->ToggleFullscreen();
++  if (!weak_ptr)
++    return;
+ 
+   // The state must change synchronously to let media react on fullscreen
+   // changes.
+diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+index af2c2f2bbc1f52f4455fb973ab2fc0d0dd013ca5..cf0c64cebe0c9c1acb789527a0406d74daeb250a 100644
+--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
++++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+@@ -463,7 +463,10 @@ void DesktopWindowTreeHostWin::FrameTypeChanged() {
+ }
+ 
+ void DesktopWindowTreeHostWin::SetFullscreen(bool fullscreen) {
++  auto weak_ptr = GetWeakPtr();
+   message_handler_->SetFullscreen(fullscreen);
++  if (!weak_ptr)
++    return;
+   // TODO(sky): workaround for ScopedFullscreenVisibility showing window
+   // directly. Instead of this should listen for visibility changes and then
+   // update window.
+diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
+index d784b8eae003ba79b109845f0d7f40c99c007791..b54f42885c976ebcd13f08ccf666447791124063 100644
+--- a/ui/views/widget/widget.cc
++++ b/ui/views/widget/widget.cc
+@@ -724,7 +724,10 @@ void Widget::SetFullscreen(bool fullscreen) {
+   if (IsFullscreen() == fullscreen)
+     return;
+ 
++  auto weak_ptr = GetWeakPtr();
+   native_widget_->SetFullscreen(fullscreen);
++  if (!weak_ptr)
++    return;
+ 
+   if (non_client_view_)
+     non_client_view_->InvalidateLayout();
+diff --git a/ui/views/win/fullscreen_handler.cc b/ui/views/win/fullscreen_handler.cc
+index 8791362556fcd7544b79982dd6535d55ecd25a50..708d28f45028ee10459c7973d51caecfe0d09097 100644
+--- a/ui/views/win/fullscreen_handler.cc
++++ b/ui/views/win/fullscreen_handler.cc
+@@ -70,6 +70,7 @@ void FullscreenHandler::SetFullscreenImpl(bool fullscreen) {
+ 
+   fullscreen_ = fullscreen;
+ 
++  auto ref = weak_ptr_factory_.GetWeakPtr();
+   if (fullscreen_) {
+     // Set new window style and size.
+     SetWindowLong(hwnd_, GWL_STYLE,
+@@ -102,6 +103,8 @@ void FullscreenHandler::SetFullscreenImpl(bool fullscreen) {
+                  new_rect.height(),
+                  SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+   }
++  if (!ref)
++    return;
+ 
+   MarkFullscreen(fullscreen);
+ }
+diff --git a/ui/views/win/fullscreen_handler.h b/ui/views/win/fullscreen_handler.h
+index fe17c7f0368b1dd35a37006033ddf34d35ea3982..c76ef18a6f59e9239d5a281d26c6e34646b68ee3 100644
+--- a/ui/views/win/fullscreen_handler.h
++++ b/ui/views/win/fullscreen_handler.h
+@@ -11,6 +11,7 @@
+ #include <map>
+ 
+ #include "base/macros.h"
++#include "base/memory/weak_ptr.h"
+ 
+ namespace gfx {
+ class Rect;
+@@ -54,6 +55,8 @@ class FullscreenHandler {
+   // Used to mark a window as fullscreen.
+   Microsoft::WRL::ComPtr<ITaskbarList2> task_bar_list_;
+ 
++  base::WeakPtrFactory<FullscreenHandler> weak_ptr_factory_{this};
++
+   DISALLOW_COPY_AND_ASSIGN(FullscreenHandler);
+ };
+ 
+diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
+index bb8b1c4fd7f1e93b6d50978ccb701393df956425..d7f91e4d70dcd7224aa8569e8773f69efac7a4e5 100644
+--- a/ui/views/win/hwnd_message_handler.cc
++++ b/ui/views/win/hwnd_message_handler.cc
+@@ -900,7 +900,10 @@ void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon,
+ 
+ void HWNDMessageHandler::SetFullscreen(bool fullscreen) {
+   background_fullscreen_hack_ = false;
++  auto ref = msg_handler_weak_factory_.GetWeakPtr();
+   fullscreen_handler()->SetFullscreen(fullscreen);
++  if (!ref)
++    return;
+ 
+   // Add the fullscreen window to the fullscreen window map which is used to
+   // handle window activations.
+@@ -1404,8 +1407,10 @@ void HWNDMessageHandler::ClientAreaSizeChanged() {
+   // Ignore size changes due to fullscreen windows losing activation.
+   if (background_fullscreen_hack_ && !sent_window_size_changing_)
+     return;
+-  gfx::Size s = GetClientAreaBounds().size();
+-  delegate_->HandleClientSizeChanged(s);
++  auto ref = msg_handler_weak_factory_.GetWeakPtr();
++  delegate_->HandleClientSizeChanged(GetClientAreaBounds().size());
++  if (!ref)
++    return;
+ 
+   current_window_size_message_++;
+   sent_window_size_changing_ = false;
+@@ -2930,8 +2935,11 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) {
+ void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) {
+   TRACE_EVENT0("ui", "HWNDMessageHandler::OnWindowPosChanged");
+ 
++  base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
+   if (DidClientAreaSizeChange(window_pos))
+     ClientAreaSizeChanged();
++  if (!ref)
++    return;
+   if (window_pos->flags & SWP_FRAMECHANGED)
+     SetDwmFrameExtension(DwmFrameState::kOn);
+   if (window_pos->flags & SWP_SHOWWINDOW) {

+ 50 - 0
patches/chromium/remove_tabs_and_line_breaks_from_the_middle_of_app_names_when.patch

@@ -0,0 +1,50 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Wed, 19 May 2021 17:11:00 +0200
+Subject: Remove tabs and line breaks from the middle of app names when
+ parsing.
+
+(cherry picked from commit f9b0a09d60acabadfcb9ddeacc9d943cc9811199)
+
+Bug: 1180126
+Change-Id: Ie6f08d45f97214c4f1ab766aa8af001b8fb8599c
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2821876
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2877715
+
+diff --git a/third_party/blink/renderer/modules/manifest/manifest_parser.cc b/third_party/blink/renderer/modules/manifest/manifest_parser.cc
+index d50f50ca92f0f622bb26f2b0a29b3fb8d70eed85..ed45e9fff703dd07c8f14f60eaa3c97a213c1b4f 100644
+--- a/third_party/blink/renderer/modules/manifest/manifest_parser.cc
++++ b/third_party/blink/renderer/modules/manifest/manifest_parser.cc
+@@ -46,6 +46,10 @@ bool URLIsWithinScope(const KURL& url, const KURL& scope) {
+          url.GetPath().StartsWith(scope.GetPath());
+ }
+ 
++static bool IsCrLfOrTabChar(UChar c) {
++  return c == '\n' || c == '\r' || c == '\t';
++}
++
+ }  // anonymous namespace
+ 
+ ManifestParser::ManifestParser(const String& data,
+@@ -256,11 +260,21 @@ KURL ManifestParser::ParseURL(const JSONObject* object,
+ 
+ String ManifestParser::ParseName(const JSONObject* object) {
+   base::Optional<String> name = ParseString(object, "name", Trim);
++  if (name.has_value()) {
++    name = name->RemoveCharacters(IsCrLfOrTabChar);
++    if (name->length() == 0)
++      name = base::nullopt;
++  }
+   return name.has_value() ? *name : String();
+ }
+ 
+ String ManifestParser::ParseShortName(const JSONObject* object) {
+   base::Optional<String> short_name = ParseString(object, "short_name", Trim);
++  if (short_name.has_value()) {
++    short_name = short_name->RemoveCharacters(IsCrLfOrTabChar);
++    if (short_name->length() == 0)
++      short_name = base::nullopt;
++  }
+   return short_name.has_value() ? *short_name : String();
+ }
+ 

+ 144 - 0
patches/chromium/replace_std_vector_with_base_observerlist_to_support_container.patch

@@ -0,0 +1,144 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Tue, 18 May 2021 23:22:27 +0200
+Subject: Replace std::vector with base::ObserverList to support container
+ modification while iterating
+
+TaskTracker saves list of viewers in vector, that needs to be notified
+when distillation is completed. At the time of notifying the viewers,
+we are indirectly erasing viewers from vector while iterating.
+
+This is causing container-overflow in asan build when vector has more
+than one viewer while notifying.
+
+This change is to replace vector with ObserverList that can be modified
+during iteration without invalidating the iterator.
+
+(cherry picked from commit be19f42dab0706d5fdd74acd6eaa424e9277e9c4)
+
+Bug: 1203590
+Change-Id: I7c7b8237584c48c9ebc2639b9268a6a78c2db4b2
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2856118
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2883743
+
+diff --git a/base/observer_list.h b/base/observer_list.h
+index 28369a25941dd62ba63a14dccfb46b8092eb6a24..a2f830191f36f74c12aa8e67db978356309e2691 100644
+--- a/base/observer_list.h
++++ b/base/observer_list.h
+@@ -272,6 +272,7 @@ class ObserverList {
+       NOTREACHED() << "Observers can only be added once!";
+       return;
+     }
++    observers_count_++;
+     observers_.emplace_back(ObserverStorageType(obs));
+   }
+ 
+@@ -284,7 +285,8 @@ class ObserverList {
+                      [obs](const auto& o) { return o.IsEqual(obs); });
+     if (it == observers_.end())
+       return;
+-
++    if (!it->IsMarkedForRemoval())
++      observers_count_--;
+     if (live_iterators_.empty()) {
+       observers_.erase(it);
+     } else {
+@@ -314,8 +316,12 @@ class ObserverList {
+       for (auto& observer : observers_)
+         observer.MarkForRemoval();
+     }
++
++    observers_count_ = 0;
+   }
+ 
++  bool empty() const { return !observers_count_; }
++
+   bool might_have_observers() const { return !observers_.empty(); }
+ 
+  private:
+@@ -334,6 +340,8 @@ class ObserverList {
+ 
+   base::LinkedList<internal::WeakLinkNode<ObserverList>> live_iterators_;
+ 
++  size_t observers_count_{0};
++
+   const ObserverListPolicy policy_;
+ 
+   SEQUENCE_CHECKER(iteration_sequence_checker_);
+diff --git a/components/dom_distiller/core/task_tracker.cc b/components/dom_distiller/core/task_tracker.cc
+index e66a62c4091e44183253ba7221db6dedcca4a1a2..f22c88967bc7d7b1a32339657b5fc2bf8248bbde 100644
+--- a/components/dom_distiller/core/task_tracker.cc
++++ b/components/dom_distiller/core/task_tracker.cc
+@@ -85,7 +85,7 @@ void TaskTracker::AddSaveCallback(SaveCallback callback) {
+ 
+ std::unique_ptr<ViewerHandle> TaskTracker::AddViewer(
+     ViewRequestDelegate* delegate) {
+-  viewers_.push_back(delegate);
++  viewers_.AddObserver(delegate);
+   if (content_ready_) {
+     // Distillation for this task has already completed, and so the delegate can
+     // be immediately told of the result.
+@@ -115,7 +115,7 @@ bool TaskTracker::HasUrl(const GURL& url) const {
+ }
+ 
+ void TaskTracker::RemoveViewer(ViewRequestDelegate* delegate) {
+-  base::Erase(viewers_, delegate);
++  viewers_.RemoveObserver(delegate);
+   if (viewers_.empty()) {
+     MaybeCancel();
+   }
+@@ -219,8 +219,8 @@ void TaskTracker::DistilledArticleReady(
+ }
+ 
+ void TaskTracker::NotifyViewersAndCallbacks() {
+-  for (auto* viewer : viewers_) {
+-    NotifyViewer(viewer);
++  for (auto& viewer : viewers_) {
++    NotifyViewer(&viewer);
+   }
+ 
+   // Already inside a callback run SaveCallbacks directly.
+@@ -242,8 +242,8 @@ void TaskTracker::DoSaveCallbacks(bool success) {
+ 
+ void TaskTracker::OnArticleDistillationUpdated(
+     const ArticleDistillationUpdate& article_update) {
+-  for (auto* viewer : viewers_) {
+-    viewer->OnArticleUpdated(article_update);
++  for (auto& viewer : viewers_) {
++    viewer.OnArticleUpdated(article_update);
+   }
+ }
+ 
+diff --git a/components/dom_distiller/core/task_tracker.h b/components/dom_distiller/core/task_tracker.h
+index 484145cf7d176fd0c3f2fa73da4cf94c23cc0bda..cc13e7272923ec3de52bcea186fdc30391c8cd2b 100644
+--- a/components/dom_distiller/core/task_tracker.h
++++ b/components/dom_distiller/core/task_tracker.h
+@@ -11,6 +11,7 @@
+ #include "base/bind.h"
+ #include "base/callback.h"
+ #include "base/memory/weak_ptr.h"
++#include "base/observer_list.h"
+ #include "components/dom_distiller/core/article_distillation_update.h"
+ #include "components/dom_distiller/core/article_entry.h"
+ #include "components/dom_distiller/core/distiller.h"
+@@ -40,9 +41,9 @@ class ViewerHandle {
+ 
+ // Interface for a DOM distiller entry viewer. Implement this to make a view
+ // request and receive the data for an entry when it becomes available.
+-class ViewRequestDelegate {
++class ViewRequestDelegate : public base::CheckedObserver {
+  public:
+-  virtual ~ViewRequestDelegate() = default;
++  ~ViewRequestDelegate() override = default;
+ 
+   // Called when the distilled article contents are available. The
+   // DistilledArticleProto is owned by a TaskTracker instance and is invalidated
+@@ -140,7 +141,7 @@ class TaskTracker {
+   std::vector<SaveCallback> save_callbacks_;
+   // A ViewRequestDelegate will be added to this list when a view request is
+   // made and removed when the corresponding ViewerHandle is destroyed.
+-  std::vector<ViewRequestDelegate*> viewers_;
++  base::ObserverList<ViewRequestDelegate> viewers_;
+ 
+   std::unique_ptr<Distiller> distiller_;
+   bool blob_fetcher_running_;

+ 3 - 0
patches/v8/.patches

@@ -26,3 +26,6 @@ lts-m86_builtins_harden_array_prototype_concat.patch
 merged_compiler_fix_a_bug_in_visitspeculativeintegeradditiveop.patch
 merged_turbofan_harden_arrayprototypepop_and_arrayprototypeshift.patch
 m86-lts_compiler_fix_off-by-one_error_in_kadditivesafeinteger.patch
+merged_wasm-simd_ia32_fix_f64x2_min_max_to_use_registers.patch
+merged_liftoff_fix_2gb_memory_accesses_on_32-bit.patch
+reland_compiler_fix_more_truncation_bugs_in_simplifiedlowering.patch

+ 120 - 0
patches/v8/merged_liftoff_fix_2gb_memory_accesses_on_32-bit.patch

@@ -0,0 +1,120 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Tue, 18 May 2021 21:05:35 +0200
+Subject: Merged: [liftoff] Fix >=2GB memory accesses on 32-bit
+
+We were inconsistent in handling offsets >= 2GB on 32-bit systems. The
+code was still relying on this being detected as statically out of
+bounds, but with the increase of {kV8MaxWasmMemoryPages} to support 4GB
+memories, this is not the case any more.
+
+This CL fixes this by again detecting such situations as statically OOB.
+We do not expect to be able to allocate memories of size >2GB on such
+systems. If this assumptions turns out to be wrong, we will erroneously
+trap. If that happens, we will have to explicitly disallow memories of
+such size on 32-bit systems.
+
+(cherry picked from commit 7ad5b961553d7d9bc30da1bb839726be2b92bb51)
+
+Bug: v8:7881, chromium:1201340
+Change-Id: Ib3d32b8d303eb047eb7811a045a8fa2b4ecb8cda
+Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2853596
+
+diff --git a/src/wasm/baseline/arm/liftoff-assembler-arm.h b/src/wasm/baseline/arm/liftoff-assembler-arm.h
+index 9379a3b78a2fc93f3558f58790f63b6bac7b6d1b..c6b4cc1276ccdc60314a0e6eb839d325eeea7ebf 100644
+--- a/src/wasm/baseline/arm/liftoff-assembler-arm.h
++++ b/src/wasm/baseline/arm/liftoff-assembler-arm.h
+@@ -689,13 +689,8 @@ void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr,
+                             Register offset_reg, uint32_t offset_imm,
+                             LoadType type, LiftoffRegList pinned,
+                             uint32_t* protected_load_pc, bool is_load_mem) {
+-  // If offset_imm cannot be converted to int32 safely, we abort as a separate
+-  // check should cause this code to never be executed.
+-  // TODO(7881): Support when >2GB is required.
+-  if (!is_uint31(offset_imm)) {
+-    TurboAssembler::Abort(AbortReason::kOffsetOutOfRange);
+-    return;
+-  }
++  // Offsets >=2GB are statically OOB on 32-bit systems.
++  DCHECK_LE(offset_imm, std::numeric_limits<int32_t>::max());
+   liftoff::LoadInternal(this, dst, src_addr, offset_reg,
+                         static_cast<int32_t>(offset_imm), type, pinned,
+                         protected_load_pc, is_load_mem);
+@@ -705,13 +700,8 @@ void LiftoffAssembler::Store(Register dst_addr, Register offset_reg,
+                              uint32_t offset_imm, LiftoffRegister src,
+                              StoreType type, LiftoffRegList pinned,
+                              uint32_t* protected_store_pc, bool is_store_mem) {
+-  // If offset_imm cannot be converted to int32 safely, we abort as a separate
+-  // check should cause this code to never be executed.
+-  // TODO(7881): Support when >2GB is required.
+-  if (!is_uint31(offset_imm)) {
+-    TurboAssembler::Abort(AbortReason::kOffsetOutOfRange);
+-    return;
+-  }
++  // Offsets >=2GB are statically OOB on 32-bit systems.
++  DCHECK_LE(offset_imm, std::numeric_limits<int32_t>::max());
+   UseScratchRegisterScope temps(this);
+   if (type.value() == StoreType::kF64Store) {
+     Register actual_dst_addr = liftoff::CalculateActualAddress(
+diff --git a/src/wasm/baseline/ia32/liftoff-assembler-ia32.h b/src/wasm/baseline/ia32/liftoff-assembler-ia32.h
+index 9bfad9313b9aa06ccee4dc50868b27007bb94fec..9005fe4303adb0cf20a23eb2f70f084bc3b08413 100644
+--- a/src/wasm/baseline/ia32/liftoff-assembler-ia32.h
++++ b/src/wasm/baseline/ia32/liftoff-assembler-ia32.h
+@@ -324,13 +324,8 @@ void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr,
+                             Register offset_reg, uint32_t offset_imm,
+                             LoadType type, LiftoffRegList pinned,
+                             uint32_t* protected_load_pc, bool is_load_mem) {
+-  if (offset_imm > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
+-    // We do not generate code here, because such an offset should never pass
+-    // the bounds check. However, the spec requires us to compile code with such
+-    // an offset.
+-    Trap();
+-    return;
+-  }
++  // Offsets >=2GB are statically OOB on 32-bit systems.
++  DCHECK_LE(offset_imm, std::numeric_limits<int32_t>::max());
+   DCHECK_EQ(type.value_type() == kWasmI64, dst.is_gp_pair());
+   Operand src_op = offset_reg == no_reg
+                        ? Operand(src_addr, offset_imm)
+@@ -406,6 +401,7 @@ void LiftoffAssembler::Store(Register dst_addr, Register offset_reg,
+                              StoreType type, LiftoffRegList pinned,
+                              uint32_t* protected_store_pc, bool is_store_mem) {
+   DCHECK_EQ(type.value_type() == kWasmI64, src.is_gp_pair());
++  // Offsets >=2GB are statically OOB on 32-bit systems.
+   DCHECK_LE(offset_imm, std::numeric_limits<int32_t>::max());
+   Operand dst_op = offset_reg == no_reg
+                        ? Operand(dst_addr, offset_imm)
+diff --git a/src/wasm/baseline/liftoff-compiler.cc b/src/wasm/baseline/liftoff-compiler.cc
+index c9625b06d8a46ee9aa632142cf9491f0e4c96bd4..8103f5b7c14f8e87ae0e175ef7bfffd25d490539 100644
+--- a/src/wasm/baseline/liftoff-compiler.cc
++++ b/src/wasm/baseline/liftoff-compiler.cc
+@@ -2100,10 +2100,7 @@ class LiftoffCompiler {
+   bool BoundsCheckMem(FullDecoder* decoder, uint32_t access_size,
+                       uint64_t offset, Register index, LiftoffRegList pinned,
+                       ForceCheck force_check) {
+-    // If the offset does not fit in a uintptr_t, this can never succeed on this
+-    // machine.
+     const bool statically_oob =
+-        offset > std::numeric_limits<uintptr_t>::max() ||
+         !base::IsInBounds<uintptr_t>(offset, access_size,
+                                      env_->max_memory_size);
+ 
+diff --git a/src/wasm/compilation-environment.h b/src/wasm/compilation-environment.h
+index d730161ad1f9d0590bc1302093e3fea48b8d230f..fc9b2fcbc2e07434b14f0ccb1e37b12550f6c9e6 100644
+--- a/src/wasm/compilation-environment.h
++++ b/src/wasm/compilation-environment.h
+@@ -63,9 +63,11 @@ struct CompilationEnv {
+ 
+   const LowerSimd lower_simd;
+ 
+-  static constexpr uint32_t kMaxMemoryPagesAtRuntime =
+-      std::min(kV8MaxWasmMemoryPages,
+-               std::numeric_limits<uintptr_t>::max() / kWasmPageSize);
++  // We assume that memories of size >= half of the virtual address space
++  // cannot be allocated (see https://crbug.com/1201340).
++  static constexpr uint32_t kMaxMemoryPagesAtRuntime = std::min(
++      kV8MaxWasmMemoryPages,
++      (uintptr_t{1} << (kSystemPointerSize == 4 ? 31 : 63)) / kWasmPageSize);
+ 
+   constexpr CompilationEnv(const WasmModule* module,
+                            UseTrapHandler use_trap_handler,

+ 40 - 0
patches/v8/merged_wasm-simd_ia32_fix_f64x2_min_max_to_use_registers.patch

@@ -0,0 +1,40 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Tue, 18 May 2021 17:59:17 +0200
+Subject: Merged: [wasm-simd][ia32] Fix f64x2 min max to use registers
+
+We don't have memory alignment yet, so using memory operands will cause
+segv if we try to access the unaligned operands (on non-AVX systems).
+
+The fix here is kept simple (the logic can be cleaned up a bit and
+optimized to not use unique registers), in order to keep the cherry-pick
+and back-merge as small and safe as possible.
+
+(cherry picked from commit 7f2d41fa3748ecc8fc888d93f82d77718b1dd6b0)
+
+Bug: chromium:1204071
+Change-Id: I7d7d177ff096ebd3de399fcf1ec7d9ac57bbb80b
+Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2872565
+
+diff --git a/src/compiler/backend/ia32/instruction-selector-ia32.cc b/src/compiler/backend/ia32/instruction-selector-ia32.cc
+index fec4053871bd14ac0f732e41e1b03eaa64a8c5cb..8a1d32e7f8096679fb265a6b7b4d84696288b8b4 100644
+--- a/src/compiler/backend/ia32/instruction-selector-ia32.cc
++++ b/src/compiler/backend/ia32/instruction-selector-ia32.cc
+@@ -2199,7 +2199,7 @@ void InstructionSelector::VisitF64x2Min(Node* node) {
+   IA32OperandGenerator g(this);
+   InstructionOperand temps[] = {g.TempSimd128Register()};
+   InstructionOperand operand0 = g.UseUniqueRegister(node->InputAt(0));
+-  InstructionOperand operand1 = g.UseUnique(node->InputAt(1));
++  InstructionOperand operand1 = g.UseUniqueRegister(node->InputAt(1));
+ 
+   if (IsSupported(AVX)) {
+     Emit(kIA32F64x2Min, g.DefineAsRegister(node), operand0, operand1,
+@@ -2214,7 +2214,7 @@ void InstructionSelector::VisitF64x2Max(Node* node) {
+   IA32OperandGenerator g(this);
+   InstructionOperand temps[] = {g.TempSimd128Register()};
+   InstructionOperand operand0 = g.UseUniqueRegister(node->InputAt(0));
+-  InstructionOperand operand1 = g.UseUnique(node->InputAt(1));
++  InstructionOperand operand1 = g.UseUniqueRegister(node->InputAt(1));
+   if (IsSupported(AVX)) {
+     Emit(kIA32F64x2Max, g.DefineAsRegister(node), operand0, operand1,
+          arraysize(temps), temps);

+ 138 - 0
patches/v8/reland_compiler_fix_more_truncation_bugs_in_simplifiedlowering.patch

@@ -0,0 +1,138 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Andrey Belenko <[email protected]>
+Date: Tue, 18 May 2021 21:52:40 +0200
+Subject: Reland "[compiler] Fix more truncation bugs in SimplifiedLowering"
+
+This is a reland of 47077d94492cb604e3a7f02c0d7c3c495ff6b713 without
+changes. The revert was false alarm.
+
+Original change's description:
+> [compiler] Fix more truncation bugs in SimplifiedLowering
+>
+> Bug: chromium:1200490
+> Change-Id: I3555b6d99bdb4b4e7c302a43a82c17e8bff84ebe
+> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2840452
+
+Bug: chromium:1200490
+Change-Id: I75cac59050bc393d157a1ee5bed776c8986a7bbe
+Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2843817
+
+diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc
+index c2f0d744c2fb698427dd876fb9a37eb280e6f583..6ed92e855c66a266e1909925caed0140a5c59a67 100644
+--- a/src/compiler/simplified-lowering.cc
++++ b/src/compiler/simplified-lowering.cc
+@@ -1398,17 +1398,32 @@ class RepresentationSelector {
+     return jsgraph_->simplified();
+   }
+ 
+-  void LowerToCheckedInt32Mul(Node* node, Truncation truncation,
+-                              Type input0_type, Type input1_type) {
+-    // If one of the inputs is positive and/or truncation is being applied,
+-    // there is no need to return -0.
+-    CheckForMinusZeroMode mz_mode =
+-        truncation.IdentifiesZeroAndMinusZero() ||
+-                IsSomePositiveOrderedNumber(input0_type) ||
+-                IsSomePositiveOrderedNumber(input1_type)
+-            ? CheckForMinusZeroMode::kDontCheckForMinusZero
+-            : CheckForMinusZeroMode::kCheckForMinusZero;
+-    NodeProperties::ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode));
++  template <Phase T>
++  void VisitForCheckedInt32Mul(Node* node, Truncation truncation,
++                               Type input0_type, Type input1_type,
++                               UseInfo input_use) {
++    DCHECK_EQ(node->opcode(), IrOpcode::kSpeculativeNumberMultiply);
++    // A -0 input is impossible or will cause a deopt.
++    DCHECK(BothInputsAre(node, Type::Signed32()) ||
++           !input_use.truncation().IdentifiesZeroAndMinusZero());
++
++    CheckForMinusZeroMode mz_mode;
++    Type restriction;
++    if (IsSomePositiveOrderedNumber(input0_type) ||
++        IsSomePositiveOrderedNumber(input1_type)) {
++      mz_mode = CheckForMinusZeroMode::kDontCheckForMinusZero;
++      restriction = Type::Signed32();
++    } else if (truncation.IdentifiesZeroAndMinusZero()) {
++      mz_mode = CheckForMinusZeroMode::kDontCheckForMinusZero;
++      restriction = Type::Signed32OrMinusZero();
++    } else {
++      mz_mode = CheckForMinusZeroMode::kCheckForMinusZero;
++      restriction = Type::Signed32();
++    }
++
++    VisitBinop<T>(node, input_use, MachineRepresentation::kWord32, restriction);
++    if (lower<T>())
++      NodeProperties::ChangeOp(node, simplified()->CheckedInt32Mul(mz_mode));
+   }
+ 
+   void ChangeToInt32OverflowOp(Node* node) {
+@@ -1600,12 +1615,22 @@ class RepresentationSelector {
+         VisitBinop<T>(node, lhs_use, rhs_use, MachineRepresentation::kWord32);
+         if (lower<T>()) DeferReplacement(node, lowering->Int32Mod(node));
+       } else if (BothInputsAre(node, Type::Unsigned32OrMinusZeroOrNaN())) {
++        Type const restriction =
++            truncation.IdentifiesZeroAndMinusZero() &&
++                    TypeOf(node->InputAt(0)).Maybe(Type::MinusZero())
++                ? Type::Unsigned32OrMinusZero()
++                : Type::Unsigned32();
+         VisitBinop<T>(node, lhs_use, rhs_use, MachineRepresentation::kWord32,
+-                      Type::Unsigned32());
++                      restriction);
+         if (lower<T>()) ChangeToUint32OverflowOp(node);
+       } else {
++        Type const restriction =
++            truncation.IdentifiesZeroAndMinusZero() &&
++                    TypeOf(node->InputAt(0)).Maybe(Type::MinusZero())
++                ? Type::Signed32OrMinusZero()
++                : Type::Signed32();
+         VisitBinop<T>(node, lhs_use, rhs_use, MachineRepresentation::kWord32,
+-                      Type::Signed32());
++                      restriction);
+         if (lower<T>()) ChangeToInt32OverflowOp(node);
+       }
+       return;
+@@ -2165,23 +2190,18 @@ class RepresentationSelector {
+           // If both inputs and feedback are int32, use the overflow op.
+           if (hint == NumberOperationHint::kSignedSmall ||
+               hint == NumberOperationHint::kSigned32) {
+-            VisitBinop<T>(node, UseInfo::TruncatingWord32(),
+-                          MachineRepresentation::kWord32, Type::Signed32());
+-            if (lower<T>()) {
+-              LowerToCheckedInt32Mul(node, truncation, input0_type,
+-                                     input1_type);
+-            }
++            VisitForCheckedInt32Mul<T>(node, truncation, input0_type,
++                                       input1_type,
++                                       UseInfo::TruncatingWord32());
+             return;
+           }
+         }
+ 
+         if (hint == NumberOperationHint::kSignedSmall ||
+             hint == NumberOperationHint::kSigned32) {
+-          VisitBinop<T>(node, CheckedUseInfoAsWord32FromHint(hint),
+-                        MachineRepresentation::kWord32, Type::Signed32());
+-          if (lower<T>()) {
+-            LowerToCheckedInt32Mul(node, truncation, input0_type, input1_type);
+-          }
++          VisitForCheckedInt32Mul<T>(node, truncation, input0_type,
++                                       input1_type,
++                                       CheckedUseInfoAsWord32FromHint(hint));
+           return;
+         }
+ 
+@@ -3901,7 +3921,6 @@ template <>
+ void RepresentationSelector::SetOutput<RETYPE>(
+     Node* node, MachineRepresentation representation, Type restriction_type) {
+   NodeInfo* const info = GetInfo(node);
+-  DCHECK(info->restriction_type().Is(restriction_type));
+   DCHECK(restriction_type.Is(info->restriction_type()));
+   info->set_output(representation);
+ }
+@@ -3911,7 +3930,6 @@ void RepresentationSelector::SetOutput<LOWER>(
+     Node* node, MachineRepresentation representation, Type restriction_type) {
+   NodeInfo* const info = GetInfo(node);
+   DCHECK_EQ(info->representation(), representation);
+-  DCHECK(info->restriction_type().Is(restriction_type));
+   DCHECK(restriction_type.Is(info->restriction_type()));
+   USE(info);
+ }