| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/paint/paint_op_reader.h" |
| |
| #include <stddef.h> |
| |
| #include "cc/paint/paint_flags.h" |
| #include "cc/paint/paint_shader.h" |
| #include "third_party/skia/include/core/SkFlattenableSerialization.h" |
| #include "third_party/skia/include/core/SkPath.h" |
| #include "third_party/skia/include/core/SkRRect.h" |
| #include "third_party/skia/include/core/SkTextBlob.h" |
| |
| namespace cc { |
| namespace { |
| |
| bool IsValidPaintShaderType(PaintShader::Type type) { |
| return static_cast<uint8_t>(type) < |
| static_cast<uint8_t>(PaintShader::Type::kShaderCount); |
| } |
| |
| bool IsValidSkShaderTileMode(SkShader::TileMode mode) { |
| return mode < SkShader::kTileModeCount; |
| } |
| |
| bool IsValidPaintShaderScalingBehavior(PaintShader::ScalingBehavior behavior) { |
| return behavior == PaintShader::ScalingBehavior::kRasterAtScale || |
| behavior == PaintShader::ScalingBehavior::kFixedScale; |
| } |
| |
| } // namespace |
| |
| template <typename T> |
| void PaintOpReader::ReadSimple(T* val) { |
| static_assert(base::is_trivially_copyable<T>::value, |
| "Not trivially copyable"); |
| if (!AlignMemory(alignof(T))) |
| valid_ = false; |
| if (remaining_bytes_ < sizeof(T)) |
| valid_ = false; |
| if (!valid_) |
| return; |
| |
| // Most of the time this is used for primitives, but this function is also |
| // used for SkRect/SkIRect/SkMatrix whose implicit operator= can't use a |
| // volatile. TOCTOU violations don't matter for these simple types so |
| // use assignment. |
| *val = *reinterpret_cast<const T*>(const_cast<const char*>(memory_)); |
| |
| memory_ += sizeof(T); |
| remaining_bytes_ -= sizeof(T); |
| } |
| |
| template <typename T> |
| void PaintOpReader::ReadFlattenable(sk_sp<T>* val) { |
| size_t bytes = 0; |
| ReadSimple(&bytes); |
| if (remaining_bytes_ < bytes) |
| valid_ = false; |
| if (!SkIsAlign4(reinterpret_cast<uintptr_t>(memory_))) |
| valid_ = false; |
| if (!valid_) |
| return; |
| if (bytes == 0) |
| return; |
| |
| // This is assumed safe from TOCTOU violations as the flattenable |
| // deserializing function uses an SkReadBuffer which reads each piece of |
| // memory once much like PaintOpReader does. |
| val->reset(static_cast<T*>(SkValidatingDeserializeFlattenable( |
| const_cast<const char*>(memory_), bytes, T::GetFlattenableType()))); |
| if (!val) |
| valid_ = false; |
| |
| memory_ += bytes; |
| remaining_bytes_ -= bytes; |
| } |
| |
| void PaintOpReader::ReadData(size_t bytes, void* data) { |
| if (remaining_bytes_ < bytes) |
| valid_ = false; |
| if (!valid_) |
| return; |
| if (bytes == 0) |
| return; |
| |
| memcpy(data, const_cast<const char*>(memory_), bytes); |
| memory_ += bytes; |
| remaining_bytes_ -= bytes; |
| } |
| |
| void PaintOpReader::ReadArray(size_t count, SkPoint* array) { |
| size_t bytes = count * sizeof(SkPoint); |
| if (remaining_bytes_ < bytes) |
| valid_ = false; |
| // Overflow? |
| if (count > static_cast<size_t>(~0) / sizeof(SkPoint)) |
| valid_ = false; |
| if (!valid_) |
| return; |
| if (count == 0) |
| return; |
| |
| memcpy(array, const_cast<const char*>(memory_), bytes); |
| memory_ += bytes; |
| remaining_bytes_ -= bytes; |
| } |
| |
| void PaintOpReader::Read(SkScalar* data) { |
| ReadSimple(data); |
| } |
| |
| void PaintOpReader::Read(size_t* data) { |
| ReadSimple(data); |
| } |
| |
| void PaintOpReader::Read(uint8_t* data) { |
| ReadSimple(data); |
| } |
| |
| void PaintOpReader::Read(SkRect* rect) { |
| ReadSimple(rect); |
| } |
| |
| void PaintOpReader::Read(SkIRect* rect) { |
| ReadSimple(rect); |
| } |
| |
| void PaintOpReader::Read(SkRRect* rect) { |
| ReadSimple(rect); |
| } |
| |
| void PaintOpReader::Read(SkPath* path) { |
| if (!valid_) |
| return; |
| |
| // This is assumed safe from TOCTOU violations as the SkPath deserializing |
| // function uses an SkRBuffer which reads each piece of memory once much |
| // like PaintOpReader does. Additionally, paths are later validated in |
| // PaintOpBuffer. |
| size_t read_bytes = |
| path->readFromMemory(const_cast<const char*>(memory_), remaining_bytes_); |
| if (!read_bytes) |
| valid_ = false; |
| |
| memory_ += read_bytes; |
| remaining_bytes_ -= read_bytes; |
| } |
| |
| void PaintOpReader::Read(PaintFlags* flags) { |
| Read(&flags->text_size_); |
| ReadSimple(&flags->color_); |
| Read(&flags->width_); |
| Read(&flags->miter_limit_); |
| ReadSimple(&flags->blend_mode_); |
| ReadSimple(&flags->bitfields_uint_); |
| |
| // TODO(enne): ReadTypeface, http://crbug.com/737629 |
| |
| // Flattenables must be read at 4-byte boundary, which should be the case |
| // here. |
| ReadFlattenable(&flags->path_effect_); |
| ReadFlattenable(&flags->mask_filter_); |
| ReadFlattenable(&flags->color_filter_); |
| ReadFlattenable(&flags->draw_looper_); |
| ReadFlattenable(&flags->image_filter_); |
| |
| Read(&flags->shader_); |
| } |
| |
| void PaintOpReader::Read(PaintImage* image) { |
| // TODO(enne): implement PaintImage serialization: http://crbug.com/737629 |
| } |
| |
| void PaintOpReader::Read(sk_sp<SkData>* data) { |
| size_t bytes = 0; |
| ReadSimple(&bytes); |
| if (remaining_bytes_ < bytes) |
| valid_ = false; |
| if (!valid_) |
| return; |
| |
| // Separate out empty vs not valid cases. |
| if (bytes == 0) { |
| bool has_data = false; |
| Read(&has_data); |
| if (has_data) |
| *data = SkData::MakeEmpty(); |
| return; |
| } |
| |
| // This is safe to cast away the volatile as it is just a memcpy internally. |
| *data = SkData::MakeWithCopy(const_cast<const char*>(memory_), bytes); |
| |
| memory_ += bytes; |
| remaining_bytes_ -= bytes; |
| } |
| |
| void PaintOpReader::Read(sk_sp<SkTextBlob>* blob) { |
| // TODO(enne): implement SkTextBlob serialization: http://crbug.com/737629 |
| } |
| |
| void PaintOpReader::Read(sk_sp<PaintShader>* shader) { |
| bool has_shader = false; |
| ReadSimple(&has_shader); |
| if (!has_shader) { |
| *shader = nullptr; |
| return; |
| } |
| PaintShader::Type shader_type; |
| ReadSimple(&shader_type); |
| // Avoid creating a shader if something is invalid. |
| if (!valid_ || !IsValidPaintShaderType(shader_type)) { |
| valid_ = false; |
| return; |
| } |
| |
| *shader = sk_sp<PaintShader>(new PaintShader(shader_type)); |
| PaintShader& ref = **shader; |
| ReadSimple(&ref.flags_); |
| ReadSimple(&ref.end_radius_); |
| ReadSimple(&ref.start_radius_); |
| ReadSimple(&ref.tx_); |
| ReadSimple(&ref.ty_); |
| if (!IsValidSkShaderTileMode(ref.tx_) || !IsValidSkShaderTileMode(ref.ty_)) |
| valid_ = false; |
| ReadSimple(&ref.fallback_color_); |
| ReadSimple(&ref.scaling_behavior_); |
| if (!IsValidPaintShaderScalingBehavior(ref.scaling_behavior_)) |
| valid_ = false; |
| bool has_local_matrix = false; |
| ReadSimple(&has_local_matrix); |
| if (has_local_matrix) { |
| ref.local_matrix_.emplace(); |
| ReadSimple(&*ref.local_matrix_); |
| } |
| ReadSimple(&ref.center_); |
| ReadSimple(&ref.tile_); |
| ReadSimple(&ref.start_point_); |
| ReadSimple(&ref.end_point_); |
| ReadSimple(&ref.start_degrees_); |
| ReadSimple(&ref.end_degrees_); |
| // TODO(vmpstr): Read PaintImage image_. http://crbug.com/737629 |
| // TODO(vmpstr): Read sk_sp<PaintRecord> record_. http://crbug.com/737629 |
| decltype(ref.colors_)::size_type colors_size = 0; |
| ReadSimple(&colors_size); |
| |
| // If colors_bytes would overflow, then definitely too many. |
| if (colors_size > std::numeric_limits<size_t>::max() / sizeof(SkColor)) { |
| valid_ = false; |
| return; |
| } |
| size_t colors_bytes = colors_size * sizeof(SkColor); |
| if (colors_bytes > remaining_bytes_) { |
| valid_ = false; |
| return; |
| } |
| // TODO(enne): maybe we should cap the number of colors or positions to |
| // something much more reasonable, e.g. 1k? |
| ref.colors_.resize(colors_size); |
| ReadData(colors_bytes, ref.colors_.data()); |
| |
| decltype(ref.positions_)::size_type positions_size = 0; |
| ReadSimple(&positions_size); |
| // TODO(enne): positions and colors have to have the same count, so maybe |
| // don't serialize this either? |
| if (positions_size != colors_size) { |
| valid_ = false; |
| return; |
| } |
| size_t positions_bytes = positions_size * sizeof(SkScalar); |
| if (positions_bytes > remaining_bytes_) { |
| valid_ = false; |
| return; |
| } |
| ref.positions_.resize(positions_size); |
| ReadData(positions_size * sizeof(SkScalar), ref.positions_.data()); |
| |
| // We don't write the cached shader, so don't attempt to read it either. |
| |
| if (!(*shader)->IsValid()) |
| valid_ = false; |
| } |
| |
| bool PaintOpReader::AlignMemory(size_t alignment) { |
| // Due to the math below, alignment must be a power of two. |
| DCHECK_GT(alignment, 0u); |
| DCHECK_EQ(alignment & (alignment - 1), 0u); |
| |
| uintptr_t memory = reinterpret_cast<uintptr_t>(memory_); |
| // The following is equivalent to: |
| // padding = (alignment - memory % alignment) % alignment; |
| // because alignment is a power of two. This doesn't use modulo operator |
| // however, since it can be slow. |
| size_t padding = ((memory + alignment - 1) & ~(alignment - 1)) - memory; |
| if (padding > remaining_bytes_) |
| return false; |
| |
| memory_ += padding; |
| remaining_bytes_ -= padding; |
| return true; |
| } |
| |
| } // namespace cc |