From 945e74805a3809d2c09fc2ca4119a4bc16e20368 Mon Sep 17 00:00:00 2001 From: ecalp-tps <72207867+ecalp-tps@users.noreply.github.com> Date: Thu, 26 Nov 2020 18:38:10 +0100 Subject: [PATCH] [Cairo] Initial integration (#4703) * Initial commit with build script and Dockerfile * Corpus and dictionary added * Some comments * Trigger build check * Remove MSan * Move glib extraction to Dockerfile * Move fuzzers into this repo. Fetch cairo from the upstream repo. * Add missing license headers * Fix dictionary and seed corpus * Replace malloc with calloc. Remove unused variable. Replace DEFINE with const. Minor fix --- projects/cairo/Dockerfile | 30 ++++++ projects/cairo/build.sh | 92 +++++++++++++++++++ projects/cairo/project.yaml | 6 ++ projects/cairo/targets/fuzzer_temp_file.h | 81 ++++++++++++++++ projects/cairo/targets/pdf_surface_fuzzer.c | 51 ++++++++++ projects/cairo/targets/raster_fuzzer.c | 71 ++++++++++++++ .../cairo/targets/surface_write_png_fuzzer.c | 46 ++++++++++ projects/cairo/targets/text_glyphs_fuzzer.c | 59 ++++++++++++ 8 files changed, 436 insertions(+) create mode 100644 projects/cairo/Dockerfile create mode 100755 projects/cairo/build.sh create mode 100644 projects/cairo/project.yaml create mode 100644 projects/cairo/targets/fuzzer_temp_file.h create mode 100644 projects/cairo/targets/pdf_surface_fuzzer.c create mode 100644 projects/cairo/targets/raster_fuzzer.c create mode 100644 projects/cairo/targets/surface_write_png_fuzzer.c create mode 100644 projects/cairo/targets/text_glyphs_fuzzer.c diff --git a/projects/cairo/Dockerfile b/projects/cairo/Dockerfile new file mode 100644 index 000000000..7463bbd45 --- /dev/null +++ b/projects/cairo/Dockerfile @@ -0,0 +1,30 @@ +# 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. +# +################################################################################ +FROM gcr.io/oss-fuzz-base/base-builder +RUN apt-get update && apt-get install -y python3-pip gtk-doc-tools libffi-dev +RUN pip3 install -U meson==0.55.3 ninja + +RUN git clone --depth 1 git://git.sv.nongnu.org/freetype/freetype2.git +ADD https://ftp.gnome.org/pub/gnome/sources/glib/2.64/glib-2.64.2.tar.xz $SRC +RUN tar xvJf $SRC/glib-2.64.2.tar.xz +RUN git clone --depth 1 https://gitlab.freedesktop.org/cairo/cairo.git && \ + zip -q $SRC/cairo_seed_corpus.zip $SRC/cairo/test/reference/* + +ADD https://raw.githubusercontent.com/google/fuzzing/master/dictionaries/png.dict $SRC/cairo.dict + +WORKDIR $SRC/cairo +COPY targets $SRC/fuzz +COPY build.sh $SRC/ diff --git a/projects/cairo/build.sh b/projects/cairo/build.sh new file mode 100755 index 000000000..655103e2a --- /dev/null +++ b/projects/cairo/build.sh @@ -0,0 +1,92 @@ +#!/bin/bash -eu +# 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. +# +################################################################################ +PREFIX=$WORK/prefix +mkdir -p $PREFIX + +export PKG_CONFIG="`which pkg-config` --static" +export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig +export PATH=$PREFIX/bin:$PATH + +BUILD=$WORK/build + +rm -rf $WORK/* +rm -rf $BUILD +mkdir -p $BUILD + +# Build glib +pushd $SRC/glib-2.64.2 +meson \ + --prefix=$PREFIX \ + --libdir=lib \ + --default-library=static \ + -Db_lundef=false \ + -Doss_fuzz=enabled \ + -Dlibmount=disabled \ + -Dinternal_pcre=true \ + _builddir +ninja -C _builddir +ninja -C _builddir install +popd + +pushd $SRC/freetype2 +./autogen.sh +./configure --prefix="$PREFIX" --disable-shared PKG_CONFIG_PATH="$PKG_CONFIG_PATH" +make -j$(nproc) +make install + +# Build cairo +pushd $SRC/cairo +meson \ + --prefix=$PREFIX \ + --libdir=lib \ + --default-library=static \ + _builddir +ninja -C _builddir +ninja -C _builddir install +popd + +mv $SRC/{*.zip,*.dict} $OUT + +if [ ! -f "${OUT}/cairo_seed_corpus.zip" ]; then + echo "missing seed corpus" + exit 1 +fi + +if [ ! -f "${OUT}/cairo.dict" ]; then + echo "missing dictionary" + exit 1 +fi + +PREDEPS_LDFLAGS="-Wl,-Bdynamic -ldl -lm -lc -pthread -lrt -lpthread" +DEPS="gmodule-2.0 glib-2.0 gio-2.0 gobject-2.0 freetype2 cairo cairo-gobject" +BUILD_CFLAGS="$CFLAGS `pkg-config --static --cflags $DEPS`" +BUILD_LDFLAGS="-Wl,-static `pkg-config --static --libs $DEPS`" + +fuzzers=$(find $SRC/fuzz/ -name "*_fuzzer.c") +for f in $fuzzers; do + fuzzer_name=$(basename $f .c) + $CC $CFLAGS $BUILD_CFLAGS \ + -c $f -o $WORK/${fuzzer_name}.o + $CXX $CXXFLAGS \ + $WORK/${fuzzer_name}.o -o $OUT/${fuzzer_name} \ + $PREDEPS_LDFLAGS \ + $BUILD_LDFLAGS \ + $LIB_FUZZING_ENGINE \ + -Wl,-Bdynamic + ln -sf $SRC/cairo_seed_corpus.zip $OUT/${fuzzer_name}_seed_corpus.zip + ln -sf $SRC/cairo.dict $OUT/${fuzzer_name}.dict +done diff --git a/projects/cairo/project.yaml b/projects/cairo/project.yaml new file mode 100644 index 000000000..37b8def43 --- /dev/null +++ b/projects/cairo/project.yaml @@ -0,0 +1,6 @@ +homepage: https://gitlab.freedesktop.org/cairo/cairo +language: c +primary_contact: security-tps@google.com +sanitizers: + - address + - undefined diff --git a/projects/cairo/targets/fuzzer_temp_file.h b/projects/cairo/targets/fuzzer_temp_file.h new file mode 100644 index 000000000..158b49781 --- /dev/null +++ b/projects/cairo/targets/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/projects/cairo/targets/pdf_surface_fuzzer.c b/projects/cairo/targets/pdf_surface_fuzzer.c new file mode 100644 index 000000000..7869f6371 --- /dev/null +++ b/projects/cairo/targets/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/projects/cairo/targets/raster_fuzzer.c b/projects/cairo/targets/raster_fuzzer.c new file mode 100644 index 000000000..e7f9bc199 --- /dev/null +++ b/projects/cairo/targets/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/projects/cairo/targets/surface_write_png_fuzzer.c b/projects/cairo/targets/surface_write_png_fuzzer.c new file mode 100644 index 000000000..9819fa287 --- /dev/null +++ b/projects/cairo/targets/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/projects/cairo/targets/text_glyphs_fuzzer.c b/projects/cairo/targets/text_glyphs_fuzzer.c new file mode 100644 index 000000000..41c180df2 --- /dev/null +++ b/projects/cairo/targets/text_glyphs_fuzzer.c @@ -0,0 +1,59 @@ +// 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; + + 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; + } + + char *buf = (char *) calloc(size + 1, sizeof(char)); + memcpy(buf, data, size); + buf[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(buf); + fuzzer_release_tmpfile(tmpfile); + return 0; +}