1 // Copyright 2018 The Wuffs Authors.
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 //    https://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 // Silence the nested slash-star warning for the next comment's command line.
18 #pragma clang diagnostic push
19 #pragma clang diagnostic ignored "-Wcomment"
20 
21 /*
22 This fuzzer (the fuzz function) is typically run indirectly, by a framework
23 such as https://github.com/google/oss-fuzz calling LLVMFuzzerTestOneInput.
24 
25 When working on the fuzz implementation, or as a sanity check, defining
26 WUFFS_CONFIG__FUZZLIB_MAIN will let you manually run fuzz over a set of files:
27 
28 gcc -DWUFFS_CONFIG__FUZZLIB_MAIN gif_fuzzer.c
29 ./a.out ../../../test/data/*.gif
30 rm -f ./a.out
31 
32 It should print "PASS", amongst other information, and exit(0).
33 */
34 
35 #pragma clang diagnostic pop
36 
37 // Wuffs ships as a "single file C library" or "header file library" as per
38 // https://github.com/nothings/stb/blob/master/docs/stb_howto.txt
39 //
40 // To use that single file as a "foo.c"-like implementation, instead of a
41 // "foo.h"-like header, #define WUFFS_IMPLEMENTATION before #include'ing or
42 // compiling it.
43 #define WUFFS_IMPLEMENTATION
44 
45 // Defining the WUFFS_CONFIG__MODULE* macros are optional, but it lets users of
46 // release/c/etc.c whitelist which parts of Wuffs to build. That file contains
47 // the entire Wuffs standard library, implementing a variety of codecs and file
48 // formats. Without this macro definition, an optimizing compiler or linker may
49 // very well discard Wuffs code for unused codecs, but listing the Wuffs
50 // modules we use makes that process explicit. Preprocessing means that such
51 // code simply isn't compiled.
52 #define WUFFS_CONFIG__MODULES
53 #define WUFFS_CONFIG__MODULE__BASE
54 #define WUFFS_CONFIG__MODULE__GIF
55 #define WUFFS_CONFIG__MODULE__LZW
56 
57 // If building this program in an environment that doesn't easily accommodate
58 // relative includes, you can use the script/inline-c-relative-includes.go
59 // program to generate a stand-alone C file.
60 #include "../../../release/c/wuffs-unsupported-snapshot.c"
61 #include "../fuzzlib/fuzzlib.c"
62 
63 const char*  //
fuzz(wuffs_base__io_buffer * src,uint64_t hash)64 fuzz(wuffs_base__io_buffer* src, uint64_t hash) {
65   const char* ret = NULL;
66   wuffs_base__slice_u8 pixbuf = ((wuffs_base__slice_u8){});
67   wuffs_base__slice_u8 workbuf = ((wuffs_base__slice_u8){});
68 
69   // Use a {} code block so that "goto exit" doesn't trigger "jump bypasses
70   // variable initialization" warnings.
71   {
72     wuffs_gif__decoder dec;
73     wuffs_base__status status = wuffs_gif__decoder__initialize(
74         &dec, sizeof dec, WUFFS_VERSION,
75         (hash & 1) ? WUFFS_INITIALIZE__LEAVE_INTERNAL_BUFFERS_UNINITIALIZED
76                    : 0);
77     if (!wuffs_base__status__is_ok(&status)) {
78       ret = wuffs_base__status__message(&status);
79       goto exit;
80     }
81 
82     wuffs_base__image_config ic = ((wuffs_base__image_config){});
83     status = wuffs_gif__decoder__decode_image_config(&dec, &ic, src);
84     if (!wuffs_base__status__is_ok(&status)) {
85       ret = wuffs_base__status__message(&status);
86       goto exit;
87     }
88     if (!wuffs_base__image_config__is_valid(&ic)) {
89       ret = "invalid image_config";
90       goto exit;
91     }
92 
93     // Wuffs allows either statically or dynamically allocated work buffers.
94     // This program exercises dynamic allocation.
95     uint64_t n = wuffs_gif__decoder__workbuf_len(&dec).max_incl;
96     if (n > 64 * 1024 * 1024) {  // Don't allocate more than 64 MiB.
97       ret = "image too large";
98       goto exit;
99     }
100     if (n > 0) {
101       workbuf = wuffs_base__malloc_slice_u8(malloc, n);
102       if (!workbuf.ptr) {
103         ret = "out of memory";
104         goto exit;
105       }
106     }
107 
108     n = wuffs_base__pixel_config__pixbuf_len(&ic.pixcfg);
109     if (n > 64 * 1024 * 1024) {  // Don't allocate more than 64 MiB.
110       ret = "image too large";
111       goto exit;
112     }
113     if (n > 0) {
114       pixbuf = wuffs_base__malloc_slice_u8(malloc, n);
115       if (!pixbuf.ptr) {
116         ret = "out of memory";
117         goto exit;
118       }
119     }
120 
121     wuffs_base__pixel_buffer pb = ((wuffs_base__pixel_buffer){});
122     status = wuffs_base__pixel_buffer__set_from_slice(&pb, &ic.pixcfg, pixbuf);
123     if (!wuffs_base__status__is_ok(&status)) {
124       ret = wuffs_base__status__message(&status);
125       goto exit;
126     }
127 
128     bool seen_ok = false;
129     while (true) {
130       wuffs_base__frame_config fc = ((wuffs_base__frame_config){});
131       status = wuffs_gif__decoder__decode_frame_config(&dec, &fc, src);
132       if (!wuffs_base__status__is_ok(&status)) {
133         if ((status.repr != wuffs_base__note__end_of_data) || !seen_ok) {
134           ret = wuffs_base__status__message(&status);
135         }
136         goto exit;
137       }
138 
139       status = wuffs_gif__decoder__decode_frame(
140           &dec, &pb, src, WUFFS_BASE__PIXEL_BLEND__SRC, workbuf, NULL);
141 
142       wuffs_base__rect_ie_u32 frame_rect =
143           wuffs_base__frame_config__bounds(&fc);
144       wuffs_base__rect_ie_u32 dirty_rect =
145           wuffs_gif__decoder__frame_dirty_rect(&dec);
146       if (!wuffs_base__rect_ie_u32__contains_rect(&frame_rect, dirty_rect)) {
147         ret = "internal error: frame_rect does not contain dirty_rect";
148         goto exit;
149       }
150 
151       if (!wuffs_base__status__is_ok(&status)) {
152         if ((status.repr != wuffs_base__note__end_of_data) || !seen_ok) {
153           ret = wuffs_base__status__message(&status);
154         }
155         goto exit;
156       }
157       seen_ok = true;
158 
159       if (!wuffs_base__rect_ie_u32__equals(&frame_rect, dirty_rect)) {
160         ret = "internal error: frame_rect does not equal dirty_rect";
161         goto exit;
162       }
163     }
164   }
165 
166 exit:
167   free(workbuf.ptr);
168   free(pixbuf.ptr);
169   return ret;
170 }
171