• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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