From 3f698db85dadb5e984bce6efc6d29ab5ed6b183a Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 15 Apr 2020 12:12:19 +0200 Subject: [PATCH] LibWeb: Limit the maximum size of bitmap buffers We will no longer create bitmap buffers for canvases that exceed a total area of (16384 * 16384) pixels. This matches what some other browser do. Thanks to @itamar8910 for finding this! :^) --- .../Bindings/HTMLCanvasElementWrapper.cpp | 4 +- .../LibWeb/DOM/CanvasRenderingContext2D.cpp | 7 +++- Libraries/LibWeb/DOM/HTMLCanvasElement.cpp | 42 +++++++++++++++---- Libraries/LibWeb/DOM/HTMLCanvasElement.h | 10 ++--- Libraries/LibWeb/Layout/LayoutCanvas.cpp | 4 +- 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/Libraries/LibWeb/Bindings/HTMLCanvasElementWrapper.cpp b/Libraries/LibWeb/Bindings/HTMLCanvasElementWrapper.cpp index b6f584df76..193ec65901 100644 --- a/Libraries/LibWeb/Bindings/HTMLCanvasElementWrapper.cpp +++ b/Libraries/LibWeb/Bindings/HTMLCanvasElementWrapper.cpp @@ -85,14 +85,14 @@ JS::Value HTMLCanvasElementWrapper::get_context(JS::Interpreter& interpreter) JS::Value HTMLCanvasElementWrapper::width_getter(JS::Interpreter& interpreter) { if (auto* impl = impl_from(interpreter)) - return JS::Value(impl->preferred_width()); + return JS::Value(impl->requested_width()); return {}; } JS::Value HTMLCanvasElementWrapper::height_getter(JS::Interpreter& interpreter) { if (auto* impl = impl_from(interpreter)) - return JS::Value(impl->preferred_height()); + return JS::Value(impl->requested_height()); return {}; } diff --git a/Libraries/LibWeb/DOM/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/DOM/CanvasRenderingContext2D.cpp index a9282a4061..92c8b3d68c 100644 --- a/Libraries/LibWeb/DOM/CanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/DOM/CanvasRenderingContext2D.cpp @@ -126,7 +126,12 @@ OwnPtr CanvasRenderingContext2D::painter() if (!m_element) return nullptr; - return make(m_element->ensure_bitmap()); + if (!m_element->bitmap()) { + if (!m_element->create_bitmap()) + return nullptr; + } + + return make(*m_element->bitmap()); } } diff --git a/Libraries/LibWeb/DOM/HTMLCanvasElement.cpp b/Libraries/LibWeb/DOM/HTMLCanvasElement.cpp index efade12919..c33f257e94 100644 --- a/Libraries/LibWeb/DOM/HTMLCanvasElement.cpp +++ b/Libraries/LibWeb/DOM/HTMLCanvasElement.cpp @@ -33,6 +33,8 @@ namespace Web { +static constexpr auto max_canvas_area = 16384 * 16384; + HTMLCanvasElement::HTMLCanvasElement(Document& document, const FlyString& tag_name) : HTMLElement(document, tag_name) { @@ -42,20 +44,20 @@ HTMLCanvasElement::~HTMLCanvasElement() { } -int HTMLCanvasElement::preferred_width() const +int HTMLCanvasElement::requested_width() const { bool ok = false; - int width = attribute("width").to_int(ok); + unsigned width = attribute("width").to_int(ok); if (ok) return width; return 300; } -int HTMLCanvasElement::preferred_height() const +int HTMLCanvasElement::requested_height() const { bool ok = false; - int height = attribute("height").to_int(ok); + unsigned height = attribute("height").to_int(ok); if (ok) return height; @@ -79,12 +81,36 @@ CanvasRenderingContext2D* HTMLCanvasElement::get_context(String type) return m_context; } -Gfx::Bitmap& HTMLCanvasElement::ensure_bitmap() +static Gfx::Size bitmap_size_for_canvas(const HTMLCanvasElement& canvas) { - if (!m_bitmap || m_bitmap->size() != Gfx::Size(preferred_width(), preferred_height())) { - m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, { preferred_width(), preferred_height() }); + int width = canvas.requested_width(); + int height = canvas.requested_height(); + if (width < 0 || height < 0) { + dbg() << "Refusing to create canvas with negative size"; + return {}; } - return *m_bitmap; + int area = 0; + if (__builtin_mul_overflow(width, height, &area)) { + dbg() << "Refusing to create " << width << "x" << height << " canvas (overflow)"; + return {}; + } + if (area > max_canvas_area) { + dbg() << "Refusing to create " << width << "x" << height << " canvas (exceeds maximum size)"; + return {}; + } + return { width, height }; +} + +bool HTMLCanvasElement::create_bitmap() +{ + auto size = bitmap_size_for_canvas(*this); + if (size.is_empty()) { + m_bitmap = nullptr; + return false; + } + if (!m_bitmap || m_bitmap->size() != size) + m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, size); + return true; } } diff --git a/Libraries/LibWeb/DOM/HTMLCanvasElement.h b/Libraries/LibWeb/DOM/HTMLCanvasElement.h index 7ac9e9195f..fb1c9d141a 100644 --- a/Libraries/LibWeb/DOM/HTMLCanvasElement.h +++ b/Libraries/LibWeb/DOM/HTMLCanvasElement.h @@ -41,14 +41,15 @@ public: HTMLCanvasElement(Document&, const FlyString& tag_name); virtual ~HTMLCanvasElement() override; - int preferred_width() const; - int preferred_height() const; - const Gfx::Bitmap* bitmap() const { return m_bitmap; } - Gfx::Bitmap& ensure_bitmap(); + Gfx::Bitmap* bitmap() { return m_bitmap; } + bool create_bitmap(); CanvasRenderingContext2D* get_context(String type); + int requested_width() const; + int requested_height() const; + private: virtual RefPtr create_layout_node(const StyleProperties* parent_style) const override; @@ -62,5 +63,4 @@ inline bool is(const Node& node) return is(node) && to(node).tag_name().equals_ignoring_case("canvas"); } - } diff --git a/Libraries/LibWeb/Layout/LayoutCanvas.cpp b/Libraries/LibWeb/Layout/LayoutCanvas.cpp index 39b81f8454..deb316ea49 100644 --- a/Libraries/LibWeb/Layout/LayoutCanvas.cpp +++ b/Libraries/LibWeb/Layout/LayoutCanvas.cpp @@ -42,8 +42,8 @@ LayoutCanvas::~LayoutCanvas() void LayoutCanvas::layout() { - rect().set_width(node().preferred_width()); - rect().set_height(node().preferred_height()); + rect().set_width(node().requested_width()); + rect().set_height(node().requested_height()); LayoutReplaced::layout(); }