Skip to content

Commit

Permalink
Ensure that Skia persistent cache filenames do not exceed the maximum…
Browse files Browse the repository at this point in the history
… allowed length (flutter#28315)
  • Loading branch information
jason-simmons authored Sep 2, 2021
1 parent a166c32 commit f8b4e8a
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 48 deletions.
3 changes: 3 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ FILE: ../../../flutter/fml/file.h
FILE: ../../../flutter/fml/file_unittest.cc
FILE: ../../../flutter/fml/hash_combine.h
FILE: ../../../flutter/fml/hash_combine_unittests.cc
FILE: ../../../flutter/fml/hex_codec.cc
FILE: ../../../flutter/fml/hex_codec.h
FILE: ../../../flutter/fml/hex_codec_unittest.cc
FILE: ../../../flutter/fml/icu_util.cc
FILE: ../../../flutter/fml/icu_util.h
FILE: ../../../flutter/fml/log_level.h
Expand Down
1 change: 1 addition & 0 deletions common/graphics/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ source_set("graphics") {
"//flutter/assets",
"//flutter/fml",
"//flutter/shell/version:version",
"//third_party/boringssl",
"//third_party/rapidjson",
"//third_party/skia",
]
Expand Down
91 changes: 64 additions & 27 deletions common/graphics/persistent_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@

#include "flutter/fml/base32.h"
#include "flutter/fml/file.h"
#include "flutter/fml/hex_codec.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/mapping.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/trace_event.h"
#include "flutter/shell/version/version.h"
#include "openssl/sha.h"
#include "rapidjson/document.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/utils/SkBase64.h"
Expand All @@ -30,21 +32,17 @@ std::shared_ptr<AssetManager> PersistentCache::asset_manager_;
std::mutex PersistentCache::instance_mutex_;
std::unique_ptr<PersistentCache> PersistentCache::gPersistentCache;

std::string PersistentCache::SkKeyToFilePath(const SkData& data) {
if (data.data() == nullptr || data.size() == 0) {
std::string PersistentCache::SkKeyToFilePath(const SkData& key) {
if (key.data() == nullptr || key.size() == 0) {
return "";
}

std::string_view view(reinterpret_cast<const char*>(data.data()),
data.size());
uint8_t sha_digest[SHA_DIGEST_LENGTH];
SHA1(static_cast<const uint8_t*>(key.data()), key.size(), sha_digest);

auto encode_result = fml::Base32Encode(view);

if (!encode_result.first) {
return "";
}

return encode_result.second;
std::string_view view(reinterpret_cast<const char*>(sha_digest),
SHA_DIGEST_LENGTH);
return fml::HexEncode(view);
}

bool PersistentCache::gIsReadOnly = false;
Expand Down Expand Up @@ -205,7 +203,7 @@ size_t PersistentCache::PrecompileKnownSkSLs(GrDirectContext* context) const {
size_t precompiled_count = 0;
for (const auto& sksl : known_sksls) {
TRACE_EVENT0("flutter", "PrecompilingSkSL");
if (context->precompileShader(*sksl.first, *sksl.second)) {
if (context->precompileShader(*sksl.key, *sksl.value)) {
precompiled_count++;
}
}
Expand All @@ -221,10 +219,9 @@ std::vector<PersistentCache::SkSLCache> PersistentCache::LoadSkSLs() const {
std::vector<PersistentCache::SkSLCache> result;
fml::FileVisitor visitor = [&result](const fml::UniqueFD& directory,
const std::string& filename) {
sk_sp<SkData> key = ParseBase32(filename);
sk_sp<SkData> data = LoadFile(directory, filename);
if (key != nullptr && data != nullptr) {
result.push_back({key, data});
SkSLCache cache = LoadFile(directory, filename, true);
if (cache.key != nullptr && cache.value != nullptr) {
result.push_back(cache);
} else {
FML_LOG(ERROR) << "Failed to load: " << filename;
}
Expand Down Expand Up @@ -291,17 +288,38 @@ bool PersistentCache::IsValid() const {
return cache_directory_ && cache_directory_->is_valid();
}

sk_sp<SkData> PersistentCache::LoadFile(const fml::UniqueFD& dir,
const std::string& file_name) {
PersistentCache::SkSLCache PersistentCache::LoadFile(
const fml::UniqueFD& dir,
const std::string& file_name,
bool need_key) {
SkSLCache result;
auto file = fml::OpenFileReadOnly(dir, file_name.c_str());
if (!file.is_valid()) {
return nullptr;
return result;
}
auto mapping = std::make_unique<fml::FileMapping>(file);
if (mapping->GetSize() == 0) {
return nullptr;
if (mapping->GetSize() < sizeof(CacheObjectHeader)) {
return result;
}
const CacheObjectHeader* header =
reinterpret_cast<const CacheObjectHeader*>(mapping->GetMapping());
if (header->signature != CacheObjectHeader::kSignature ||
header->version != CacheObjectHeader::kVersion1) {
FML_LOG(INFO) << "Persistent cache header is corrupt: " << file_name;
return result;
}
if (mapping->GetSize() < sizeof(CacheObjectHeader) + header->key_size) {
FML_LOG(INFO) << "Persistent cache size is corrupt: " << file_name;
return result;
}
return SkData::MakeWithCopy(mapping->GetMapping(), mapping->GetSize());
if (need_key) {
result.key = SkData::MakeWithCopy(
mapping->GetMapping() + sizeof(CacheObjectHeader), header->key_size);
}
size_t value_offset = sizeof(CacheObjectHeader) + header->key_size;
result.value = SkData::MakeWithCopy(mapping->GetMapping() + value_offset,
mapping->GetSize() - value_offset);
return result;
}

// |GrContextOptions::PersistentCache|
Expand All @@ -314,7 +332,8 @@ sk_sp<SkData> PersistentCache::load(const SkData& key) {
if (file_name.size() == 0) {
return nullptr;
}
auto result = PersistentCache::LoadFile(*cache_directory_, file_name);
auto result =
PersistentCache::LoadFile(*cache_directory_, file_name, false).value;
if (result != nullptr) {
TRACE_EVENT0("flutter", "PersistentCacheLoadHit");
}
Expand Down Expand Up @@ -349,6 +368,26 @@ static void PersistentCacheStore(fml::RefPtr<fml::TaskRunner> worker,
}
}

std::unique_ptr<fml::MallocMapping> PersistentCache::BuildCacheObject(
const SkData& key,
const SkData& data) {
size_t total_size = sizeof(CacheObjectHeader) + key.size() + data.size();
uint8_t* mapping_buf = reinterpret_cast<uint8_t*>(malloc(total_size));
if (!mapping_buf) {
return nullptr;
}
auto mapping = std::make_unique<fml::MallocMapping>(mapping_buf, total_size);

CacheObjectHeader header(key.size());
memcpy(mapping_buf, &header, sizeof(CacheObjectHeader));
mapping_buf += sizeof(CacheObjectHeader);
memcpy(mapping_buf, key.data(), key.size());
mapping_buf += key.size();
memcpy(mapping_buf, data.data(), data.size());

return mapping;
}

// |GrContextOptions::PersistentCache|
void PersistentCache::store(const SkData& key, const SkData& data) {
stored_new_shaders_ = true;
Expand All @@ -367,10 +406,8 @@ void PersistentCache::store(const SkData& key, const SkData& data) {
return;
}

auto mapping = std::make_unique<fml::DataMapping>(
std::vector<uint8_t>{data.bytes(), data.bytes() + data.size()});

if (mapping == nullptr || mapping->GetSize() == 0) {
std::unique_ptr<fml::MallocMapping> mapping = BuildCacheObject(key, data);
if (!mapping) {
return;
}

Expand Down
31 changes: 27 additions & 4 deletions common/graphics/persistent_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,26 @@ class PersistentCache : public GrContextOptions::PersistentCache {
//
// This is used to specify persistent cache filenames and service protocol
// json keys.
static std::string SkKeyToFilePath(const SkData& data);
static std::string SkKeyToFilePath(const SkData& key);

// Allocate a MallocMapping containing the given key and value in the file
// format used by the cache.
static std::unique_ptr<fml::MallocMapping> BuildCacheObject(
const SkData& key,
const SkData& data);

// Header written into the files used to store cached Skia objects.
struct CacheObjectHeader {
// A prefix used to identify the cache object file format.
static const uint32_t kSignature = 0xA869593F;
static const uint32_t kVersion1 = 1;

CacheObjectHeader(uint32_t p_key_size) : key_size(p_key_size) {}

uint32_t signature = kSignature;
uint32_t version = kVersion1;
uint32_t key_size;
};

~PersistentCache() override;

Expand All @@ -69,7 +88,10 @@ class PersistentCache : public GrContextOptions::PersistentCache {
// |GrContextOptions::PersistentCache|
sk_sp<SkData> load(const SkData& key) override;

using SkSLCache = std::pair<sk_sp<SkData>, sk_sp<SkData>>;
struct SkSLCache {
sk_sp<SkData> key;
sk_sp<SkData> value;
};

/// Load all the SkSL shader caches in the right directory.
std::vector<SkSLCache> LoadSkSLs() const;
Expand Down Expand Up @@ -135,8 +157,9 @@ class PersistentCache : public GrContextOptions::PersistentCache {
bool stored_new_shaders_ = false;
bool is_dumping_skp_ = false;

static sk_sp<SkData> LoadFile(const fml::UniqueFD& dir,
const std::string& filen_ame);
static SkSLCache LoadFile(const fml::UniqueFD& dir,
const std::string& file_name,
bool need_key);

bool IsValid() const;

Expand Down
3 changes: 3 additions & 0 deletions fml/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ source_set("fml") {
"file.cc",
"file.h",
"hash_combine.h",
"hex_codec.cc",
"hex_codec.h",
"icu_util.cc",
"icu_util.h",
"log_level.h",
Expand Down Expand Up @@ -269,6 +271,7 @@ if (enable_unittests) {
"command_line_unittest.cc",
"file_unittest.cc",
"hash_combine_unittests.cc",
"hex_codec_unittest.cc",
"logging_unittests.cc",
"mapping_unittests.cc",
"memory/ref_counted_unittest.cc",
Expand Down
24 changes: 24 additions & 0 deletions fml/hex_codec.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2013 The Flutter 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 "flutter/fml/base32.h"

#include <string>

namespace fml {

static constexpr char kEncoding[] = "0123456789abcdef";

std::string HexEncode(std::string_view input) {
std::string result;
result.reserve(input.size() * 2);
for (char c : input) {
uint8_t b = static_cast<uint8_t>(c);
result.push_back(kEncoding[b >> 4]);
result.push_back(kEncoding[b & 0xF]);
}
return result;
}

} // namespace fml
16 changes: 16 additions & 0 deletions fml/hex_codec.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_FML_HEX_CODEC_H_
#define FLUTTER_FML_HEX_CODEC_H_

#include <string_view>

namespace fml {

std::string HexEncode(std::string_view input);

} // namespace fml

#endif // FLUTTER_FML_HEX_CODEC_H_
31 changes: 31 additions & 0 deletions fml/hex_codec_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2013 The Flutter 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 "flutter/fml/hex_codec.h"

#include <iostream>

#include "gtest/gtest.h"

TEST(HexCodecTest, CanEncode) {
{
auto result = fml::HexEncode("hello");
ASSERT_EQ(result, "68656c6c6f");
}

{
auto result = fml::HexEncode("");
ASSERT_EQ(result, "");
}

{
auto result = fml::HexEncode("1");
ASSERT_EQ(result, "31");
}

{
auto result = fml::HexEncode(std::string_view("\xFF\xFE\x00\x01", 4));
ASSERT_EQ(result, "fffe0001");
}
}
10 changes: 5 additions & 5 deletions shell/common/persistent_cache_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,14 @@ TEST_F(PersistentCacheTest, CanLoadSkSLsFromAsset) {

// Make sure that the 2 shaders are sorted by their keys. Their keys should
// be "A" and "B" (decoded from "II" and "IE").
if (shaders[0].first->bytes()[0] == 'B') {
if (shaders[0].key->bytes()[0] == 'B') {
std::swap(shaders[0], shaders[1]);
}

CheckTextSkData(shaders[0].first, "A");
CheckTextSkData(shaders[1].first, "B");
CheckTextSkData(shaders[0].second, "x");
CheckTextSkData(shaders[1].second, "y");
CheckTextSkData(shaders[0].key, "A");
CheckTextSkData(shaders[1].key, "B");
CheckTextSkData(shaders[0].value, "x");
CheckTextSkData(shaders[1].value, "y");
}

// Cleanup.
Expand Down
14 changes: 10 additions & 4 deletions shell/common/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "flutter/assets/directory_asset_bundle.h"
#include "flutter/common/graphics/persistent_cache.h"
#include "flutter/fml/base32.h"
#include "flutter/fml/file.h"
#include "flutter/fml/icu_util.h"
#include "flutter/fml/log_settings.h"
Expand Down Expand Up @@ -1679,14 +1680,19 @@ bool Shell::OnServiceProtocolGetSkSLs(
std::vector<PersistentCache::SkSLCache> sksls = persistent_cache->LoadSkSLs();
for (const auto& sksl : sksls) {
size_t b64_size =
SkBase64::Encode(sksl.second->data(), sksl.second->size(), nullptr);
SkBase64::Encode(sksl.value->data(), sksl.value->size(), nullptr);
sk_sp<SkData> b64_data = SkData::MakeUninitialized(b64_size + 1);
char* b64_char = static_cast<char*>(b64_data->writable_data());
SkBase64::Encode(sksl.second->data(), sksl.second->size(), b64_char);
SkBase64::Encode(sksl.value->data(), sksl.value->size(), b64_char);
b64_char[b64_size] = 0; // make it null terminated for printing
rapidjson::Value shader_value(b64_char, response->GetAllocator());
rapidjson::Value shader_key(PersistentCache::SkKeyToFilePath(*sksl.first),
response->GetAllocator());
std::string_view key_view(reinterpret_cast<const char*>(sksl.key->data()),
sksl.key->size());
auto encode_result = fml::Base32Encode(key_view);
if (!encode_result.first) {
continue;
}
rapidjson::Value shader_key(encode_result.second, response->GetAllocator());
shaders_json.AddMember(shader_key, shader_value, response->GetAllocator());
}
response->AddMember("SkSLs", shaders_json, response->GetAllocator());
Expand Down
Loading

0 comments on commit f8b4e8a

Please sign in to comment.