1 // Copyright 2018 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef TESTING_LIBFUZZER_PROTO_SKIA_IMAGE_FILTER_PROTO_CONVERTER_H_ 6 #define TESTING_LIBFUZZER_PROTO_SKIA_IMAGE_FILTER_PROTO_CONVERTER_H_ 7 8 #include <random> 9 #include <set> 10 #include <string> 11 #include <tuple> 12 #include <unordered_map> 13 #include <vector> 14 15 #include "third_party/skia/include/core/SkPoint.h" 16 17 #include "testing/libfuzzer/proto/skia_image_filter.pb.h" 18 19 using google::protobuf::FieldDescriptor; 20 using google::protobuf::Message; 21 using google::protobuf::Reflection; 22 23 typedef std::unordered_map<std::string, std::string> string_map_t; 24 25 namespace skia_image_filter_proto_converter { 26 27 // Takes an Input proto as input and converts it to a string that will usually 28 // be deserialized as a skia image filter. 29 class Converter { 30 public: 31 Converter(); 32 Converter(const Converter&); 33 ~Converter(); 34 35 // Provides the public interface for this class's functionality by converting 36 // Input to a string representing a serialized image filter. 37 std::string Convert(const Input&); 38 39 private: 40 // These constexprs are copied from skia. 41 static constexpr uint8_t kICC_Flag = 1 << 1; 42 static constexpr size_t kICCTagTableEntrySize = 12; 43 static constexpr uint32_t kMatrix_Flag = 1 << 0; 44 static constexpr uint8_t k0_Version = 0; 45 static constexpr uint8_t kTransferFn_Flag = 1 << 3; 46 static constexpr size_t kLut8InputSize = 48; 47 static constexpr size_t kOneChannelGammasSize = 256; 48 static constexpr size_t kMaxLut16GammaEntries = 4096; 49 static constexpr uint8_t kLut8Precision = 1; 50 static const uint32_t kPictEofTag; 51 static const uint32_t kProfileLookupTable[]; 52 static const uint32_t kInputColorSpaceLookupTable[]; 53 static const uint32_t kPCSLookupTable[]; 54 static const uint32_t kTagLookupTable[]; 55 static const char kPictureMagicString[]; 56 static const char kSkPictReaderTag[]; 57 static const uint8_t kCountNibBits[]; 58 59 // The size of kColorTableBuffer. 60 static const int kColorTableBufferLength; 61 62 // Used to bound flattenable_depth_. 63 static const int kFlattenableDepthLimit; 64 65 // Used to bound numeric fields. 66 static const int kNumBound; 67 68 // Used by ColorTableToArray to store a ColorTable Message as an array. 69 static uint8_t kColorTableBuffer[]; 70 71 // There will be a 1/kMutateEnumDenominator chance that WriteEnum 72 // writes an invalid enum value instead of the one given to us by LPM. 73 // This must be greater than 1. 74 static const uint8_t kMutateEnumDenominator; 75 76 // Mapping of field names to types. 77 static const string_map_t kFieldToFlattenableName; 78 79 // Used by IsBlacklisted to determine which skia flattenable should not be 80 // serialized. 81 static const std::set<std::string> kMisbehavedFlattenableBlacklist; 82 83 // Probably the most important attribute, a char vector that contains 84 // serialized skia flattenable written by the Visit functions. The contents of 85 // output_ is returned by Convert(). 86 std::vector<char> output_; 87 88 // Stores the size of output_ when a skia flattenable is being written (since 89 // they need to store their size). 90 std::vector<size_t> start_sizes_; 91 92 // Keep a record of whether we used kStrokeAndFill_Style or kStroke_Style 93 // already since using them multiple times increases the risk of OOMs and 94 // timeouts. 95 bool stroke_style_used_; 96 97 // Used to keep track of how nested are the skia flattenables we are writing. 98 // We use this to limit nesting to avoid OOMs and timeouts. 99 int flattenable_depth_; 100 101 // Nesting PairPathEffects is particularly likely to cause OOMs and timeouts 102 // so limit this even more than other skia flattenables. 103 int pair_path_effect_depth_; 104 105 // Don't allow ComposeColorFilters to contain themselves or else LPM will go 106 // crazy and nest them to the point that it causes OOMs and timeouts (these 107 // filters are more likely than other skia flattenables to cause these 108 // problems). 109 bool in_compose_color_filter_; 110 111 // Used to generate random numbers (for replacing NaNs, and mutating enum 112 // values). 113 std::mt19937 rand_gen_; 114 115 // A distribution from 2-kMutateEnumDenominator that will be used 116 // to generate a random value that we will use to decide if an enum value 117 // should be written as-is or if it should be mutated. 118 // The reason why there is a 2/kMutateEnumDenominator chance rather than a 119 // 1/kMutateEnumDenominator chance is because we treat making an enum value 120 // too small as a separate case from making it too big. 121 std::uniform_int_distribution<> enum_mutator_chance_distribution_; 122 123 // Prevents WriteEnum from writing an invalid enum value instead of the one it 124 // was given. 125 bool dont_mutate_enum_; 126 127 // BoundNum and BoundFloat will only return positive numbers when this is 128 // true. 129 bool bound_positive_; 130 131 // In production we don't need attributes used by ICC code since it is not 132 // built for production code. 133 #ifdef DEVELOPMENT 134 uint32_t icc_base_; 135 int tag_offset_; 136 #endif // DEVELOPMENT 137 138 // Reset any state used by the flattenable so that is can be used to convert 139 // another proto input. 140 void Reset(); 141 142 void Visit(const PictureImageFilter&); 143 void Visit(const Picture&); 144 void Visit(const PictureTagChild&); 145 146 // The generic Visit function. The compiler will allow this to be called on 147 // any proto message, though this won't result in a correct conversion. 148 // However some very simple messages have contents that can pretty much be 149 // written as they are defined by the fields of msg (eg "DistantLight"). This 150 // method is intended for those messages and will properly convert 151 // those. Note that this method is viral in that any fields on msg that 152 // contain other messages will only be written using this method and 153 // WriteFields. For example, "DistantLight" has a field "direction" that 154 // contains a "Point3" message. It is OK to call this on "DistantLight" 155 // messages because it is OK to call this on "Point3". In essence, it 156 // is only correct to call this method on msg if it is correct to call this 157 // method on any fields (or fields of fields etc.) of msg that are also 158 // Messages. See the file comment on skia_image_filter.proto for an 159 // explanation of how this and WriteFields are used for autovisit. 160 void Visit(const Message& msg); 161 162 void Visit(const PictureData&); 163 void Visit(const RecordingData&); 164 void Visit(const LightParent&); 165 void Visit(const ImageFilterChild&); 166 void Visit(const ImageFilterParent&, const int num_inputs_required); 167 void Visit(const MatrixImageFilter&); 168 void Visit(const Matrix&, bool is_local = false); 169 void Visit(const SpecularLightingImageFilter&); 170 void Visit(const PaintImageFilter&); 171 void Visit(const Paint&); 172 void Visit(const PaintEffects&); 173 void Visit(const PathEffectChild&); 174 void Visit(const LooperChild&); 175 void Visit(const LayerDrawLooper&); 176 void Visit(const LayerInfo&); 177 void Visit(const ColorFilterChild&); 178 void Visit(const ComposeColorFilter&); 179 void Visit(const OverdrawColorFilter&); 180 void Visit(const ToSRGBColorFilter&); 181 void Visit(const ColorFilterMatrix&); 182 void Visit(const ColorMatrixFilterRowMajor255&); 183 void Visit(const MergeImageFilter&); 184 void Visit(const XfermodeImageFilter&); 185 void Visit(const DiffuseLightingImageFilter&); 186 void Visit(const XfermodeImageFilter_Base&); 187 void Visit(const TileImageFilter&); 188 void Visit(const OffsetImageFilter&); 189 void Visit(const ErodeImageFilter&); 190 void Visit(const DilateImageFilter&); 191 void Visit(const DiscretePathEffect&); 192 void Visit(const MatrixConvolutionImageFilter&); 193 void Visit(const MagnifierImageFilter&); 194 void Visit(const LocalMatrixImageFilter&); 195 void Visit(const ImageSource&); 196 void Visit(const Path&); 197 void Visit(const PathRef&); 198 void Visit(const DropShadowImageFilter&); 199 void Visit(const DisplacementMapEffect&); 200 void Visit(const ComposeImageFilter&); 201 void Visit(const ColorFilterImageFilter&); 202 void Visit(const BlurImageFilterImpl&); 203 void Visit(const AlphaThresholdFilterImpl&); 204 void Visit(const Region&); 205 void Visit(const Path1DPathEffect&); 206 207 // Writes the correct PairPathEffect skia flattenable (SkSumPathEffect or 208 // SkComposePathEffect) depending on pair.type(). Note that it writes the 209 // entire skia flattenable (including name and size) unlike most Visit 210 // functions and thus should not be be preceded by a call to 211 // PreVisitFlattenable, nor should it be followed by a call to 212 // PostVisitFlattenable. 213 void Visit(const PairPathEffect&); 214 215 void Visit(const ShaderChild&); 216 void Visit(const Color4Shader&); 217 void Visit(const GradientDescriptor&); 218 void Visit(const GradientParent&); 219 void Visit(const TwoPointConicalGradient&); 220 void Visit(const LinearGradient&); 221 void Visit(const SweepGradient&); 222 void Visit(const RadialGradient&); 223 void Visit(const PictureShader&); 224 void Visit(const LocalMatrixShader&); 225 void Visit(const ComposeShader&); 226 void Visit(const ColorFilterShader&); 227 void Visit(const ImageShader&); 228 void Visit(const Color4f&); 229 void Visit(const Image&); 230 void Visit(const ImageData&); 231 void Visit(const ColorSpaceChild&); 232 void Visit(const ColorSpace_XYZ&); 233 void Visit(const ColorSpaceNamed&); 234 void Visit(const TransferFn&); 235 void Visit(const MaskFilterChild&); 236 void Visit(const Table_ColorFilter&); 237 void Visit(const EmbossMaskFilter&); 238 void Visit(const EmbossMaskFilterLight&); 239 void Visit(const DashImpl&); 240 void Visit(const Path2DPathEffect&); 241 void Visit(const ArithmeticImageFilter&); 242 void Visit(const LightChild&); 243 void Visit(const CropRectangle&); 244 void Visit(const Rectangle&); 245 void Visit(const PictureInfo&); 246 void Visit(const BlurMaskFilter&); 247 void Visit(const HighContrast_Filter&); 248 void Visit(const ReaderPictureTag&); 249 void Visit(const Vertices&); 250 void Visit(const TextBlob&); 251 template <class T> 252 void Visit(const google::protobuf::RepeatedPtrField<T>& repeated_field); 253 void VisitPictureTag(const PathPictureTag& path_picture_tag, uint32_t tag); 254 void VisitPictureTag(const PaintPictureTag& paint_picture_tag, uint32_t tag); 255 template <class T> 256 void VisitPictureTag(const T& picture_tag_child, uint32_t tag); 257 258 // Returns false if there is too much nesting (determined by 259 // kFlattenableDepthLimit and flattenable_depth_). Writes name and reserves a 260 // space to write the size of the flattenable. Also increments 261 // flattenable_depth_. 262 bool PreVisitFlattenable(const std::string& name); 263 264 // Writes the size of the flattenable to the reserved space, ensures that 265 // output_ is four byte aligned and then decrements flattenable_depth_. 266 void PostVisitFlattenable(); 267 268 std::tuple<int32_t, int32_t, int32_t, int32_t> WriteNonEmptyIRect( 269 const IRect& irect); 270 271 void WriteColorSpaceVersion(); 272 // Write a string in the proper serialized format, padding if necessary. 273 void WriteString(std::string str); 274 275 // Get the size of a skia flattenable that was just written and insert it at 276 // the proper location. Every call to this method should have a corresponding 277 // call to RecordSize. 278 void WriteBytesWritten(); 279 280 // Reserves space to write the size of what we are serializing and records 281 // info so that WriteBytesWritten can determine the size. Every call to this 282 // method should have a corresponding call to WriteBytesWritten that it is 283 // followed by. 284 void RecordSize(); 285 286 // Write size to position in output_. 287 void InsertSize(const size_t size, const uint32_t position); 288 289 // Pops off the end of start_sizes_. 290 size_t PopStartSize(); 291 292 // Pad the write_size bytes that were written with zeroes so that the 293 // write_size + number of padding bytes is divisible by four. 294 void Pad(const size_t write_size); 295 296 // Write size elements of RepeatedField of uint32_ts repeated_field as an 297 // array. 298 void WriteArray( 299 const google::protobuf::RepeatedField<uint32_t>& repeated_field, 300 const size_t size); 301 302 // Write size bytes of arr as an array and pad if necessary. 303 void WriteArray(const char* arr, const size_t size); 304 305 void WriteBool(const bool bool_val); 306 307 // Write the fields of msg starting with the field whose number is start and 308 // ending with the field whose number is end. If end is 0 then all fields 309 // until the last one will be written. start defaults to 1 and end defaults to 310 // 0. Note that not all possible (eg repeated bools) fields are supported, 311 // consult the code to determine if the field is supported (an error will be 312 // thrown if a field is unsupported). Note that WriteFields is viral in that 313 // if msg contains a field containing another Message, let's say msg2, then 314 // WriteFields(msg2) will be called (assuming that fields is in the range of 315 // msgs we are writing). If there is a method defined Visit(const Message2& 316 // msg2), it will not be called because there is no simple way to determine 317 // the type of msg2 using protobuf's reflection API. WriteFields will bound 318 // any numeric fields before writing them to avoid OOMs and timeouts. See the 319 // file comment on skia_image_filter.proto for an explanation of how this and 320 // the generic Visit function are used for autovisit. Note that this may write 321 // invalid enum values instead of the ones provided if dont_mutate_enum_ is 322 // true. Note that this method may not work if the max field of msg has number 323 // that is different than the number of fields. For example, if msg does not 324 // have a field with field number 1, but has fields with field numbers 2 and 325 // 3, calling WriteFields(msg, 2) will not write field 3. 326 void WriteFields(const Message& msg, 327 const unsigned start = 1, 328 const unsigned end = 0); 329 330 // Given the name of a proto field, field_name returns the name of the 331 // flattenable skia flattenable object it represents. 332 std::string FieldToFlattenableName(const std::string& field_name) const; 333 334 void CheckAlignment() const; 335 // Append our proto Message proto_point to sk_points as an SkPoint. 336 void AppendAsSkPoint(std::vector<SkPoint>& sk_points, 337 const Point& proto_point) const; 338 339 template <typename T> 340 void WriteUInt8(T num); 341 void WriteUInt16(uint16_t num); 342 void WriteNum(const char (&num_arr)[4]); 343 // Write num as a number. Assumes num is four bytes or less. 344 template <class T> 345 void WriteNum(T num); 346 347 // Write the enum value described by field descriptor and stored on message, 348 // or write an invalid enum value with some probability if dont_mutate_enums_ 349 // is false. 350 void WriteEnum(const Message& msg, 351 const Reflection* reflection, 352 const FieldDescriptor* field_descriptor); 353 354 // Bound a num using num_bound so that it won't cause OOMs or timeouts. 355 template <typename T> 356 T BoundNum(T num, int upper_bound) const; 357 358 // kNumBound cant be a default parameter to BoundNum(T num, int upper_bound) 359 // so this function exists instead. 360 template <typename T> 361 T BoundNum(T num); 362 363 // kNumBound cant be a default parameter to BoundFloat(T num, int upper_bound) 364 // so this function exists instead. 365 float BoundFloat(float num); 366 367 // Bound a float num using kNumBound so that it won't cause OOMs or 368 // timeouts. 369 float BoundFloat(float num, const float num_bound); 370 371 // Convert input_num to a uint8_t. 372 template <typename T> 373 uint8_t ToUInt8(const T input_num) const; 374 375 float GetRandomFloat(std::mt19937* gen_ptr); 376 float GetRandomFloat(float seed, float min, float max); 377 378 // Write a sane value of field_value, which should be the value of a field of 379 // a matrix. 380 void WriteMatrixField(float field_value); 381 382 // Saturating wrapper for stdlib.h's abs, which has undefined behavior when 383 // given INT_MIN. This returns abs(INT_MIN+1) if val is INT_MIN. 384 int Abs(const int val) const; 385 386 // Writes the representation of a rectangle returned by GetValidRectangle. 387 template <typename T> 388 void WriteRectangle(std::tuple<T, T, T, T> rectangle); 389 390 // Bound the points making up a rectangle so that the returned tuple is a 391 // valid rectangle. 392 std::tuple<float, float, float, float> GetValidRectangle(float left, 393 float top, 394 float right, 395 float bottom); 396 397 std::tuple<int32_t, int32_t, int32_t, int32_t> GetValidIRect(int32_t left, 398 int32_t top, 399 int32_t right, 400 int32_t bottom); 401 402 bool IsFinite(float num) const; 403 bool IsBlacklisted(const std::string& field_name) const; 404 405 // Converts color_table from our proto Message format to a 256-byte array. 406 // Note that this function modifies kColorTableBuffer. 407 const uint8_t* ColorTableToArray(const ColorTable& color_table); 408 409 #ifdef DEVELOPMENT 410 // ICC related functions 411 void Visit(const ICC&); 412 void Visit(const ICCColorSpace&); 413 void Visit(const ICCXYZ&); 414 void Visit(const ICCGray&); 415 void Visit(const ICCA2B0&); 416 void Visit(const ICCA2B0AToB&); 417 void Visit(const ICCA2B0Lut8&); 418 void Visit(const ICCA2B0Lut16&); 419 uint8_t GetClutGridPoints(const ICCA2B0Lut8&); 420 uint32_t GetCurrentICCOffset(); 421 uint32_t GetLut8Size(const ICCA2B0Lut8&); 422 uint32_t GetLut16Size(const ICCA2B0Lut16&); 423 void WriteLut8(const ICCA2B0Lut8&); 424 void WriteA2B0TagCommon(); 425 void WriteTagSize(const char (&tag)[4], const size_t size); 426 template <typename T> 427 void WriteBigEndian(const T num); 428 void WriteTagHeader(const uint32_t tag, const uint32_t len); 429 430 // Write num_fields zeroes to fill the space used by ignored or reserved 431 // fields in an ICC ColorSpace. 432 void WriteIgnoredFields(const int num_fields); 433 434 // Bound illuminant using num to avoid OOMs and timeouts. 435 int32_t BoundIlluminant(float illuminant, const float num) const; 436 437 // ImageInfo related functions 438 void Visit(const ImageInfo&, const int32_t width, const int32_t height); 439 std::tuple<int32_t, int32_t, int32_t> 440 GetNumPixelBytes(const ImageInfo& image_info, int32_t width, int32_t height); 441 442 size_t ComputeMinByteSize(int32_t width, 443 int32_t height, 444 ImageInfo::AlphaType alpha_type) const; 445 #endif // DEVELOPMENT 446 }; 447 } // namespace skia_image_filter_proto_converter 448 #endif // TESTING_LIBFUZZER_PROTO_SKIA_IMAGE_FILTER_PROTO_CONVERTER_H_ 449