diff --git a/fuzzing/fuzzer_temp_file.h b/fuzzing/fuzzer_temp_file.h new file mode 100644 index 000000000..158b49781 --- /dev/null +++ b/fuzzing/fuzzer_temp_file.h @@ -0,0 +1,81 @@ +// Copyright 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Adapter utility from fuzzer input to a temporary file, for fuzzing APIs that +// require a file instead of an input buffer. + +#ifndef FUZZER_TEMP_FILE_H_ +#define FUZZER_TEMP_FILE_H_ + +#include +#include +#include +#include +#include + +// Pure-C interface for creating and cleaning up temporary files. + +static char* fuzzer_get_tmpfile(const uint8_t* data, size_t size) { + char* filename_buffer = strdup("/tmp/generate_temporary_file.XXXXXX"); + if (!filename_buffer) { + perror("Failed to allocate file name buffer."); + abort(); + } + const int file_descriptor = mkstemp(filename_buffer); + if (file_descriptor < 0) { + perror("Failed to make temporary file."); + abort(); + } + FILE* file = fdopen(file_descriptor, "wb"); + if (!file) { + perror("Failed to open file descriptor."); + close(file_descriptor); + abort(); + } + const size_t bytes_written = fwrite(data, sizeof(uint8_t), size, file); + if (bytes_written < size) { + close(file_descriptor); + fprintf(stderr, "Failed to write all bytes to file (%zu out of %zu)", + bytes_written, size); + abort(); + } + fclose(file); + return filename_buffer; +} + +static void fuzzer_release_tmpfile(char* filename) { + if (unlink(filename) != 0) { + perror("WARNING: Failed to delete temporary file."); + } + free(filename); +} + +// C++ RAII object for creating temporary files. + +#ifdef __cplusplus +class FuzzerTemporaryFile { + public: + FuzzerTemporaryFile(const uint8_t* data, size_t size) + : filename_(fuzzer_get_tmpfile(data, size)) {} + + ~FuzzerTemporaryFile() { fuzzer_release_tmpfile(filename_); } + + const char* filename() const { return filename_; } + + private: + char* filename_; +}; +#endif + +#endif // FUZZER_TEMP_FILE_H_ diff --git a/fuzzing/meson.build b/fuzzing/meson.build new file mode 100644 index 000000000..282dc34de --- /dev/null +++ b/fuzzing/meson.build @@ -0,0 +1,18 @@ +fuzz_targets = [ + 'raster_fuzzer', + 'pdf_surface_fuzzer', + 'text_glyphs_fuzzer', + 'surface_write_png_fuzzer', +] + +fuzz_args = ['-fsanitize=fuzzer,address'] + +foreach target_name : fuzz_targets + exe = executable(target_name, [target_name + '.c'], + include_directories: [incbase, incsrc], + c_args: [fuzz_args, '-DHAVE_CONFIG_H'], + link_with: [libcairo], + link_args: [fuzz_args, extra_link_args], + dependencies: [deps, test_deps], + ) +endforeach diff --git a/fuzzing/pdf_surface_fuzzer.c b/fuzzing/pdf_surface_fuzzer.c new file mode 100644 index 000000000..7869f6371 --- /dev/null +++ b/fuzzing/pdf_surface_fuzzer.c @@ -0,0 +1,51 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include "fuzzer_temp_file.h" + +const double width_in_inches = 3; +const double height_in_inches = 3; +const double width_in_points = width_in_inches * 72.0; +const double height_in_points = height_in_inches * 72.0; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + cairo_t *cr; + cairo_surface_t *surface; + cairo_status_t status; + + char *tmpfile = fuzzer_get_tmpfile(data, size); + surface = cairo_pdf_surface_create(tmpfile, width_in_points, height_in_points); + status = cairo_surface_status(surface); + if (status != CAIRO_STATUS_SUCCESS) { + fuzzer_release_tmpfile(tmpfile); + return 0; + } + + char *buf = (char *) calloc(size + 1, sizeof(char)); + memcpy(buf, data, size); + buf[size] = '\0'; + + cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_TITLE, buf); + cr = cairo_create(surface); + cairo_tag_begin(cr, buf, NULL); + cairo_tag_end(cr, buf); + + cairo_destroy(cr); + cairo_surface_destroy(surface); + free(buf); + fuzzer_release_tmpfile(tmpfile); + return 0; +} diff --git a/fuzzing/raster_fuzzer.c b/fuzzing/raster_fuzzer.c new file mode 100644 index 000000000..e7f9bc199 --- /dev/null +++ b/fuzzing/raster_fuzzer.c @@ -0,0 +1,71 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include "fuzzer_temp_file.h" + +static cairo_surface_t * +acquire (cairo_pattern_t *pattern, void *closure, + cairo_surface_t *target, + const cairo_rectangle_int_t *extents) +{ + return cairo_image_surface_create_from_png(closure); +} + +static void +release (cairo_pattern_t *pattern, void *closure, cairo_surface_t *surface) +{ + cairo_surface_destroy(surface); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + cairo_t *cr; + cairo_surface_t *surface; + cairo_pattern_t *pattern; + cairo_content_t content; + cairo_status_t status; + int w, h; + + char *tmpfile = fuzzer_get_tmpfile(data, size); + surface = cairo_image_surface_create_from_png(tmpfile); + status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) { + fuzzer_release_tmpfile(tmpfile); + return 0; + } + + cr = cairo_create(surface); + content = cairo_surface_get_content(surface); + w = cairo_image_surface_get_width(surface); + h = cairo_image_surface_get_height(surface); + + char *buf = (char *) calloc(size + 1, sizeof(char)); + memcpy(buf, data, size); + buf[size] = '\0'; + + pattern = cairo_pattern_create_raster_source(buf, content, w, h); + cairo_raster_source_pattern_set_acquire (pattern, acquire, release); + cairo_set_source(cr, pattern); + cairo_pdf_surface_set_page_label(surface, buf); + cairo_pdf_surface_set_metadata(surface, CAIRO_PDF_METADATA_KEYWORDS, buf); + cairo_paint(cr); + + cairo_destroy(cr); + cairo_pattern_destroy(pattern); + cairo_surface_destroy(surface); + free(buf); + fuzzer_release_tmpfile(tmpfile); + return 0; +} diff --git a/fuzzing/surface_write_png_fuzzer.c b/fuzzing/surface_write_png_fuzzer.c new file mode 100644 index 000000000..9819fa287 --- /dev/null +++ b/fuzzing/surface_write_png_fuzzer.c @@ -0,0 +1,46 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "fuzzer_temp_file.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + cairo_surface_t *image; + cairo_surface_t *surface; + cairo_status_t status; + cairo_format_t format; + + char *tmpfile = fuzzer_get_tmpfile(data, size); + image = cairo_image_surface_create_from_png(tmpfile); + status = cairo_surface_status (image); + if (status != CAIRO_STATUS_SUCCESS) { + fuzzer_release_tmpfile(tmpfile); + return 0; + } + + format = cairo_image_surface_get_format(image); + surface = cairo_image_surface_create_for_data((unsigned char*)data, format, 1, 1, size); + status = cairo_surface_status (surface); + if (status != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(image); + fuzzer_release_tmpfile(tmpfile); + return 0; + } + cairo_surface_write_to_png(surface, tmpfile); + + cairo_surface_destroy(surface); + cairo_surface_destroy(image); + fuzzer_release_tmpfile(tmpfile); + return 0; +} diff --git a/fuzzing/text_glyphs_fuzzer.c b/fuzzing/text_glyphs_fuzzer.c new file mode 100644 index 000000000..5cc2b03d7 --- /dev/null +++ b/fuzzing/text_glyphs_fuzzer.c @@ -0,0 +1,65 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "fuzzer_temp_file.h" + +const int glyph_range = 9; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < glyph_range) { + return 0; + } + cairo_t *cr; + cairo_surface_t *surface; + cairo_status_t status; + cairo_text_extents_t extents; + cairo_text_cluster_t cluster; + size_t new_size = size - glyph_range; + uint8_t *new_data = (uint8_t *) calloc(new_size, sizeof(uint8_t)); + // Skip first glyph_range number of bytes + memcpy(new_data, &data[glyph_range], new_size * sizeof(uint8_t)); + + char *tmpfile = fuzzer_get_tmpfile(new_data, new_size); + surface = cairo_image_surface_create_from_png(tmpfile); + status = cairo_surface_status(surface); + if (status != CAIRO_STATUS_SUCCESS) { + free(new_data); + fuzzer_release_tmpfile(tmpfile); + return 0; + } + + char *buf = (char *) calloc(new_size + 1, sizeof(char)); + memcpy(buf, new_data, new_size); + buf[new_size] = '\0'; + + cr = cairo_create(surface); + cairo_text_extents(cr, buf, &extents); + cluster.num_bytes = size; + cluster.num_glyphs = 1; + for (int i = 0; i < glyph_range; i++) { + // Taken from test/text-glyph-range.c + cairo_glyph_t glyph = { + (long int)data[i], 10 * i, 25 + }; + cairo_show_text_glyphs(cr, buf, size, &glyph, 1, &cluster, 1, 0); + } + + cairo_destroy(cr); + cairo_surface_destroy(surface); + free(new_data); + free(buf); + fuzzer_release_tmpfile(tmpfile); + return 0; +} diff --git a/meson.build b/meson.build index 48fd6bb13..5ce3b7f0a 100644 --- a/meson.build +++ b/meson.build @@ -840,6 +840,8 @@ if get_option('gtk_doc') subdir('doc/public') endif +subdir('fuzzing') + configure_file(output: 'config.h', configuration: conf) foreach feature: built_features