blob: 1b1c681de9f162af3d46cefb0d0c414b6760857b [file] [log] [blame]
// 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