/src/libwebp/tests/fuzzer/fuzz_utils.cc
Line | Count | Source |
1 | | // Copyright 2024 Google LLC |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | //////////////////////////////////////////////////////////////////////////////// |
16 | | |
17 | | #include "./fuzz_utils.h" |
18 | | |
19 | | #include <algorithm> |
20 | | #include <cassert> |
21 | | #include <cstddef> |
22 | | #include <cstdint> |
23 | | #include <cstdlib> |
24 | | #include <string> |
25 | | #include <string_view> |
26 | | #include <tuple> |
27 | | #include <vector> |
28 | | |
29 | | #include "./img_alpha.h" |
30 | | #include "./img_grid.h" |
31 | | #include "./img_peak.h" |
32 | | #include "src/dsp/cpu.h" |
33 | | #include "webp/decode.h" |
34 | | #include "webp/encode.h" |
35 | | #include "webp/types.h" |
36 | | |
37 | | namespace fuzz_utils { |
38 | | |
39 | 27.1k | WebPPicture GetSourcePicture(int image_index, bool use_argb) { |
40 | 27.1k | WebPPicture pic; |
41 | 27.1k | if (!WebPPictureInit(&pic)) std::abort(); |
42 | 27.1k | pic.use_argb = use_argb; |
43 | | |
44 | | // Pick a source picture. |
45 | 27.1k | const int kImagesWidth[] = {kImgAlphaWidth, kImgGridWidth, kImgPeakWidth}; |
46 | 27.1k | const int kImagesHeight[] = {kImgAlphaHeight, kImgGridHeight, kImgPeakHeight}; |
47 | 27.1k | const uint8_t* const image_data = kImagesData[image_index]; |
48 | 27.1k | pic.width = kImagesWidth[image_index]; |
49 | 27.1k | pic.height = kImagesHeight[image_index]; |
50 | 27.1k | pic.argb_stride = pic.width * 4 * sizeof(uint8_t); |
51 | | |
52 | | // Read the bytes. |
53 | 27.1k | if (!WebPPictureImportRGBA(&pic, image_data, pic.argb_stride)) std::abort(); |
54 | 27.1k | return pic; |
55 | 27.1k | } |
56 | | |
57 | | //------------------------------------------------------------------------------ |
58 | | |
59 | 87.6k | int CropOrScale(WebPPicture* const pic, const CropOrScaleParams& params) { |
60 | 87.6k | if (pic == NULL) return 0; |
61 | 87.6k | #if !defined(WEBP_REDUCE_SIZE) |
62 | 87.6k | if (params.alter_input) { |
63 | 37.7k | if (params.crop_or_scale) { |
64 | 7.53k | const int cropped_width = std::max(1, pic->width / params.width_ratio); |
65 | 7.53k | const int cropped_height = std::max(1, pic->height / params.height_ratio); |
66 | 7.53k | const int cropped_left = (pic->width - cropped_width) / params.left_ratio; |
67 | 7.53k | const int cropped_top = (pic->height - cropped_height) / params.top_ratio; |
68 | 7.53k | return WebPPictureCrop(pic, cropped_left, cropped_top, cropped_width, |
69 | 7.53k | cropped_height); |
70 | 30.1k | } else { |
71 | 30.1k | const int scaled_width = 1 + (pic->width * params.width_ratio) / 8; |
72 | 30.1k | const int scaled_height = 1 + (pic->height * params.height_ratio) / 8; |
73 | 30.1k | return WebPPictureRescale(pic, scaled_width, scaled_height); |
74 | 30.1k | } |
75 | 37.7k | } |
76 | | #else // defined(WEBP_REDUCE_SIZE) |
77 | | (void)pic; |
78 | | (void)params; |
79 | | #endif // !defined(WEBP_REDUCE_SIZE) |
80 | 49.9k | return 1; |
81 | 87.6k | } |
82 | | |
83 | | extern "C" VP8CPUInfo VP8GetCPUInfo; |
84 | | static VP8CPUInfo GetCPUInfo; |
85 | | |
86 | 180k | static WEBP_INLINE int GetCPUInfoNoSSE41(CPUFeature feature) { |
87 | 180k | if (feature == kSSE4_1 || feature == kAVX) return 0; |
88 | 106k | return GetCPUInfo(feature); |
89 | 180k | } |
90 | | |
91 | 152k | static WEBP_INLINE int GetCPUInfoNoAVX(CPUFeature feature) { |
92 | 152k | if (feature == kAVX) return 0; |
93 | 152k | return GetCPUInfo(feature); |
94 | 152k | } |
95 | | |
96 | 166k | static WEBP_INLINE int GetCPUInfoForceSlowSSSE3(CPUFeature feature) { |
97 | 166k | if (feature == kSlowSSSE3 && GetCPUInfo(kSSE3)) { |
98 | 4.99k | return 1; // we have SSE3 -> force SlowSSSE3 |
99 | 4.99k | } |
100 | 161k | return GetCPUInfo(feature); |
101 | 166k | } |
102 | | |
103 | 185k | static WEBP_INLINE int GetCPUInfoOnlyC(CPUFeature feature) { |
104 | 185k | (void)feature; |
105 | 185k | return 0; |
106 | 185k | } |
107 | | |
108 | 87.6k | void SetOptimization(VP8CPUInfo default_VP8GetCPUInfo, uint32_t index) { |
109 | 87.6k | assert(index <= kMaxOptimizationIndex); |
110 | 87.6k | GetCPUInfo = default_VP8GetCPUInfo; |
111 | 87.6k | const VP8CPUInfo kVP8CPUInfos[kMaxOptimizationIndex + 1] = { |
112 | 87.6k | GetCPUInfoOnlyC, GetCPUInfoForceSlowSSSE3, GetCPUInfoNoSSE41, |
113 | 87.6k | GetCPUInfoNoAVX, GetCPUInfo}; |
114 | 87.6k | VP8GetCPUInfo = kVP8CPUInfos[index]; |
115 | 87.6k | } |
116 | | |
117 | | //------------------------------------------------------------------------------ |
118 | | |
119 | 0 | std::vector<std::string> ReadFilesFromDirectory(std::string_view dir) { |
120 | 0 | std::vector<std::tuple<std::string>> tuples = |
121 | 0 | fuzztest::ReadFilesFromDirectory(dir); |
122 | 0 | std::vector<std::string> strings(tuples.size()); |
123 | 0 | for (size_t i = 0; i < tuples.size(); ++i) { |
124 | 0 | using std::swap; |
125 | 0 | swap(std::get<0>(tuples[i]), strings[i]); |
126 | 0 | } |
127 | 0 | return strings; |
128 | 0 | } |
129 | | |
130 | | //------------------------------------------------------------------------------ |
131 | | // The code in this section is copied from |
132 | | // https://github.com/webmproject/sjpeg/blob/ |
133 | | // 1c025b3dbc2246de3e1d7c287970f1a01291800f/src/jpeg_tools.cc#L47 |
134 | | // (same license as this file). |
135 | | |
136 | | namespace { |
137 | | // Constants below are marker codes defined in JPEG spec |
138 | | // ISO/IEC 10918-1 : 1993(E) Table B.1 |
139 | | // See also: http://www.w3.org/Graphics/JPEG/itu-t81.pdf |
140 | | |
141 | 12.1k | #define M_SOF0 0xffc0 |
142 | 5.73k | #define M_SOF1 0xffc1 |
143 | | |
144 | 10.0k | const uint8_t* GetSOFData(const uint8_t* src, int size) { |
145 | 10.0k | if (src == NULL) return NULL; |
146 | 10.0k | const uint8_t* const end = src + size - 8; // 8 bytes of safety, for marker |
147 | 10.0k | src += 2; // skip M_SOI |
148 | 8.56M | for (; src < end && *src != 0xff; ++src) { /* search first 0xff marker */ |
149 | 8.55M | } |
150 | 15.6k | while (src < end) { |
151 | 6.05k | const uint32_t marker = static_cast<uint32_t>((src[0] << 8) | src[1]); |
152 | 6.05k | if (marker == M_SOF0 || marker == M_SOF1) return src; |
153 | 5.60k | const size_t s = 2 + ((src[2] << 8) | src[3]); |
154 | 5.60k | src += s; |
155 | 5.60k | } |
156 | 9.56k | return NULL; // No SOF marker found |
157 | 10.0k | } |
158 | | |
159 | | bool SjpegDimensions(const uint8_t* src0, size_t size, int* width, int* height, |
160 | 10.0k | int* is_yuv420) { |
161 | 10.0k | if (width == NULL || height == NULL) return false; |
162 | 10.0k | const uint8_t* src = GetSOFData(src0, size); |
163 | 10.0k | const size_t left_over = size - (src - src0); |
164 | 10.0k | if (src == NULL || left_over < 8 + 3 * 1) return false; |
165 | 449 | if (height != NULL) *height = (src[5] << 8) | src[6]; |
166 | 449 | if (width != NULL) *width = (src[7] << 8) | src[8]; |
167 | 449 | if (is_yuv420 != NULL) { |
168 | 449 | const size_t nb_comps = src[9]; |
169 | 449 | *is_yuv420 = (nb_comps == 3); |
170 | 449 | if (left_over < 11 + 3 * nb_comps) return false; |
171 | 436 | for (int c = 0; *is_yuv420 && c < 3; ++c) { |
172 | 8 | const int expected_dim = (c == 0 ? 0x22 : 0x11); |
173 | 8 | *is_yuv420 &= (src[11 + c * 3] == expected_dim); |
174 | 8 | } |
175 | 428 | } |
176 | 428 | return true; |
177 | 449 | } |
178 | | } // namespace |
179 | | |
180 | | //------------------------------------------------------------------------------ |
181 | | |
182 | 10.0k | bool IsImageTooBig(const uint8_t* data, size_t size) { |
183 | 10.0k | int width, height, components; |
184 | 10.0k | if (SjpegDimensions(data, size, &width, &height, &components) || |
185 | 9.62k | WebPGetInfo(data, size, &width, &height)) { |
186 | | // Look at the number of 8x8px blocks rather than the overall pixel count |
187 | | // when comparing to memory and duration thresholds. |
188 | 6.64k | const size_t ceiled_width = ((size_t)width + 7) / 8 * 8; |
189 | 6.64k | const size_t ceiled_height = ((size_t)height + 7) / 8 * 8; |
190 | | // Threshold to avoid out-of-memory and timeout issues. |
191 | | // The threshold is arbitrary but below the fuzzer limit of 2 GB. |
192 | | // The value cannot be 2 GB because of the added memory by MSAN. |
193 | 6.64k | if (ceiled_width * ceiled_height > kFuzzPxLimit) return true; |
194 | 6.64k | } |
195 | 9.98k | return false; |
196 | 10.0k | } |
197 | | |
198 | | } // namespace fuzz_utils |