image_sanitizer.cc 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/browser/image_sanitizer.h"
  5. #include "base/bind.h"
  6. #include "base/debug/dump_without_crashing.h"
  7. #include "base/files/file_util.h"
  8. #include "base/task/task_runner_util.h"
  9. #include "extensions/browser/extension_file_task_runner.h"
  10. #include "extensions/common/extension_resource_path_normalizer.h"
  11. #include "services/data_decoder/public/cpp/decode_image.h"
  12. #include "ui/gfx/codec/png_codec.h"
  13. namespace extensions {
  14. namespace {
  15. // We don't expect icons and other extension's images to be big.
  16. // We use this limit to prevent from opening too large images.
  17. const int kMaxImageCanvas = 4096 * 4096; // 16MB
  18. // Reads the file in |path| and then deletes it.
  19. // Returns a tuple containing: the file content, whether the read was
  20. // successful, whether the delete was successful.
  21. std::tuple<std::vector<uint8_t>, bool, bool> ReadAndDeleteBinaryFile(
  22. const base::FilePath& path) {
  23. std::vector<uint8_t> contents;
  24. bool read_success = false;
  25. int64_t file_size;
  26. if (base::GetFileSize(path, &file_size)) {
  27. contents.resize(file_size);
  28. read_success =
  29. base::ReadFile(path, reinterpret_cast<char*>(contents.data()),
  30. file_size) == file_size;
  31. }
  32. bool delete_success = base::DeleteFile(path);
  33. return std::make_tuple(std::move(contents), read_success, delete_success);
  34. }
  35. std::pair<bool, std::vector<unsigned char>> EncodeImage(const SkBitmap& image) {
  36. std::vector<unsigned char> image_data;
  37. bool success = gfx::PNGCodec::EncodeBGRASkBitmap(
  38. image,
  39. /*discard_transparency=*/false, &image_data);
  40. return std::make_pair(success, std::move(image_data));
  41. }
  42. int WriteFile(const base::FilePath& path,
  43. const std::vector<unsigned char>& data) {
  44. return base::WriteFile(path, reinterpret_cast<const char*>(data.data()),
  45. base::checked_cast<int>(data.size()));
  46. }
  47. } // namespace
  48. // static
  49. std::unique_ptr<ImageSanitizer> ImageSanitizer::CreateAndStart(
  50. scoped_refptr<Client> client,
  51. const base::FilePath& image_dir,
  52. const std::set<base::FilePath>& image_paths,
  53. const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) {
  54. std::unique_ptr<ImageSanitizer> sanitizer(
  55. new ImageSanitizer(client, image_dir, image_paths, io_task_runner));
  56. sanitizer->Start();
  57. return sanitizer;
  58. }
  59. ImageSanitizer::ImageSanitizer(
  60. scoped_refptr<Client> client,
  61. const base::FilePath& image_dir,
  62. const std::set<base::FilePath>& image_relative_paths,
  63. const scoped_refptr<base::SequencedTaskRunner>& io_task_runner)
  64. : image_dir_(image_dir),
  65. image_paths_(image_relative_paths),
  66. client_(std::move(client)),
  67. io_task_runner_(io_task_runner) {
  68. DCHECK(client_);
  69. }
  70. ImageSanitizer::~ImageSanitizer() = default;
  71. ImageSanitizer::Client::~Client() = default;
  72. void ImageSanitizer::Start() {
  73. if (image_paths_.empty()) {
  74. base::SequencedTaskRunnerHandle::Get()->PostTask(
  75. FROM_HERE, base::BindOnce(&ImageSanitizer::ReportSuccess,
  76. weak_factory_.GetWeakPtr()));
  77. return;
  78. }
  79. std::set<base::FilePath> normalized_image_paths;
  80. for (const base::FilePath& path : image_paths_) {
  81. // Normalize paths as |image_paths_| can contain duplicates like "icon.png"
  82. // and "./icon.png" to avoid unpacking the same image twice.
  83. base::FilePath normalized_path;
  84. if (path.IsAbsolute() || path.ReferencesParent() ||
  85. !NormalizeExtensionResourcePath(path, &normalized_path)) {
  86. // Report the error asynchronously so the caller stack has chance to
  87. // unwind.
  88. base::SequencedTaskRunnerHandle::Get()->PostTask(
  89. FROM_HERE, base::BindOnce(&ImageSanitizer::ReportError,
  90. weak_factory_.GetWeakPtr(),
  91. Status::kImagePathError, path));
  92. return;
  93. }
  94. normalized_image_paths.insert(normalized_path);
  95. }
  96. // Update |image_paths_| as some of the path might have been changed by
  97. // normalization.
  98. image_paths_ = std::move(normalized_image_paths);
  99. // Note that we use 2 for loops instead of one to prevent a race and flakyness
  100. // in tests: if |image_paths_| contains 2 paths, a valid one that points to a
  101. // file that does not exist and an invalid one, there is a race that can cause
  102. // either error to be reported (kImagePathError or kFileReadError).
  103. for (const base::FilePath& path : image_paths_) {
  104. base::FilePath full_image_path = image_dir_.Append(path);
  105. base::PostTaskAndReplyWithResult(
  106. io_task_runner_.get(), FROM_HERE,
  107. base::BindOnce(&ReadAndDeleteBinaryFile, full_image_path),
  108. base::BindOnce(&ImageSanitizer::ImageFileRead,
  109. weak_factory_.GetWeakPtr(), path));
  110. }
  111. }
  112. void ImageSanitizer::ImageFileRead(
  113. const base::FilePath& image_path,
  114. std::tuple<std::vector<uint8_t>, bool, bool> read_and_delete_result) {
  115. if (!std::get<1>(read_and_delete_result)) {
  116. ReportError(Status::kFileReadError, image_path);
  117. return;
  118. }
  119. if (!std::get<2>(read_and_delete_result)) {
  120. ReportError(Status::kFileDeleteError, image_path);
  121. return;
  122. }
  123. const std::vector<uint8_t>& image_data = std::get<0>(read_and_delete_result);
  124. data_decoder::DecodeImage(
  125. client_->GetDataDecoder(), image_data,
  126. data_decoder::mojom::ImageCodec::kDefault,
  127. /*shrink_to_fit=*/false, kMaxImageCanvas, gfx::Size(),
  128. base::BindOnce(&ImageSanitizer::ImageDecoded, weak_factory_.GetWeakPtr(),
  129. image_path));
  130. }
  131. void ImageSanitizer::ImageDecoded(const base::FilePath& image_path,
  132. const SkBitmap& decoded_image) {
  133. if (decoded_image.isNull()) {
  134. ReportError(Status::kDecodingError, image_path);
  135. return;
  136. }
  137. if (decoded_image.colorType() != kN32_SkColorType) {
  138. // The renderer should be sending us N32 32bpp bitmaps in reply, otherwise
  139. // this can lead to out-of-bounds mistakes when transferring the pixels out
  140. // of the bitmap into other buffers.
  141. base::debug::DumpWithoutCrashing();
  142. ReportError(Status::kDecodingError, image_path);
  143. return;
  144. }
  145. // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
  146. // though they may originally be .jpg, etc. Figure something out.
  147. // http://code.google.com/p/chromium/issues/detail?id=12459
  148. base::PostTaskAndReplyWithResult(
  149. io_task_runner_.get(), FROM_HERE,
  150. base::BindOnce(&EncodeImage, decoded_image),
  151. base::BindOnce(&ImageSanitizer::ImageReencoded,
  152. weak_factory_.GetWeakPtr(), image_path));
  153. client_->OnImageDecoded(image_path, decoded_image);
  154. // Note that the `client` callback could potentially delete `this` object.
  155. }
  156. void ImageSanitizer::ImageReencoded(
  157. const base::FilePath& image_path,
  158. std::pair<bool, std::vector<unsigned char>> result) {
  159. bool success = result.first;
  160. std::vector<unsigned char> image_data = std::move(result.second);
  161. if (!success) {
  162. ReportError(Status::kEncodingError, image_path);
  163. return;
  164. }
  165. int size = base::checked_cast<int>(image_data.size());
  166. base::PostTaskAndReplyWithResult(
  167. io_task_runner_.get(), FROM_HERE,
  168. base::BindOnce(&WriteFile, image_dir_.Append(image_path),
  169. std::move(image_data)),
  170. base::BindOnce(&ImageSanitizer::ImageWritten, weak_factory_.GetWeakPtr(),
  171. image_path, size));
  172. }
  173. void ImageSanitizer::ImageWritten(const base::FilePath& image_path,
  174. int expected_size,
  175. int actual_size) {
  176. if (expected_size != actual_size) {
  177. ReportError(Status::kFileWriteError, image_path);
  178. return;
  179. }
  180. // We have finished with this path.
  181. size_t removed_count = image_paths_.erase(image_path);
  182. DCHECK_EQ(1U, removed_count);
  183. if (image_paths_.empty()) {
  184. // This was the last path, we are done.
  185. ReportSuccess();
  186. }
  187. }
  188. void ImageSanitizer::ReportSuccess() {
  189. // Reset `client_` early, before the callback potentially deletes `this`.
  190. scoped_refptr<Client> client = std::move(client_);
  191. DCHECK(!client_);
  192. // The `client_` callback is the last statement, because it can potentially
  193. // delete `this` object.
  194. client->OnImageSanitizationDone(Status::kSuccess, base::FilePath());
  195. }
  196. void ImageSanitizer::ReportError(Status status, const base::FilePath& path) {
  197. // Prevent any other task from reporting, we want to notify only once.
  198. weak_factory_.InvalidateWeakPtrs();
  199. // Reset `client_` early, before the callback potentially deletes `this`.
  200. scoped_refptr<Client> client = std::move(client_);
  201. DCHECK(!client_);
  202. // The `client_` callback is the last statement, because it can potentially
  203. // delete `this` object.
  204. client->OnImageSanitizationDone(status, path);
  205. }
  206. } // namespace extensions