diff --git a/projects/firefox/Dockerfile b/projects/firefox/Dockerfile new file mode 100644 index 000000000..690f5ca7b --- /dev/null +++ b/projects/firefox/Dockerfile @@ -0,0 +1,26 @@ +# 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 +MAINTAINER pdknsk@gmail.com +RUN apt-get update && \ + apt-get install -y apt-file gawk mercurial parallel && \ + apt-file --non-interactive update +RUN hg clone https://hg.mozilla.org/mozilla-central +WORKDIR mozilla-central +ENV SHELL /bin/bash # required by mach for some reason +RUN ./mach bootstrap --no-interactive --application-choice browser +COPY build.sh target.c $SRC/ diff --git a/projects/firefox/build.sh b/projects/firefox/build.sh new file mode 100755 index 000000000..80e8b5c3d --- /dev/null +++ b/projects/firefox/build.sh @@ -0,0 +1,96 @@ +#!/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. +# +################################################################################ + +# Case-sensitive names of internal Firefox fuzzing targets. Edit to add more. +FUZZ_TARGETS=( + SdpParser + StunParser + # Qcms # needn't be enabled; has its own project with more sanitizers/engines +) + +# Firefox object (build) directory. +OBJDIR=$WORK/obj-fuzz + +# Firefox fuzzing build configuration. +cat << EOF > mozconfig +ac_add_options --disable-debug +ac_add_options --disable-elf-hack +ac_add_options --disable-jemalloc +ac_add_options --disable-crashreporter +ac_add_options --enable-fuzzing +ac_add_options --enable-optimize=-O1 +ac_add_options --enable-debug-symbols=-gline-tables-only +mk_add_options MOZ_OBJDIR=${OBJDIR} +mk_add_options MOZ_MAKE_FLAGS=-j$(nproc) +mk_add_options CFLAGS= +mk_add_options CXXFLAGS= +EOF + +if [[ $SANITIZER = "address" ]] +then +cat << EOF >> mozconfig +ac_add_options --enable-address-sanitizer +EOF +fi + +# Build! Takes about 15 minutes on a 32 vCPU instance. +./mach build +./mach gtest buildbutdontrun + +# Packages Firefox only to immediately extract the archive. Some files are +# replaced with gtest-variants, which is required by the fuzzing interface. +# Weighs in shy of 1GB afterwards. +make -j$(nproc) -C $OBJDIR package +tar -xf $OBJDIR/dist/firefox*bz2 -C $OUT +mv $OBJDIR/toolkit/library/gtest/libxul.so $OUT/firefox +mv $OUT/firefox/dependentlibs.list $OUT/firefox/dependentlibs.list.gtest + +# Get the absolute paths of the required libraries. +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$OUT/firefox +REQUIRED_LIBRARIES=($(ldd $OUT/firefox/libxul.so | gawk '/=> [/]/ {print $3}')) +REQUIRED_LIBRARIES=(${REQUIRED_LIBRARIES[@]##$OUT/*}) + +mkdir $WORK/apt +chown _apt $WORK/apt # suppress warning message on each file +cd $WORK/apt + +# Find and download packages which have the required files, ignoring some. +# Note that apt-file is very slow, hence parallel is used. +# Takes only 1-2 minutes on a 32 vCPU instance. +PACKAGES=($(parallel apt-file search -lFN "{}" ::: ${REQUIRED_LIBRARIES[@]})) +PACKAGES=(${PACKAGES[@]##libc6*}) +PACKAGES=(${PACKAGES[@]##libstdc++*}) +apt-get -q download ${PACKAGES[@]} + +mkdir $WORK/deb +# Extract downloaded packages. +find $WORK/apt -type f -exec dpkg-deb --extract "{}" $WORK/deb \; + +mkdir $OUT/lib +# Move required libraries (and symlinks). Less than 50MB total. +for REQUIRED_LIBRARY in ${REQUIRED_LIBRARIES[@]} +do + find $WORK/deb -name "${REQUIRED_LIBRARY##*/}*" -exec mv "{}" $OUT/lib \; +done + +# Build a wrapper binary for each target to set environment variables. +for FUZZ_TARGET in ${FUZZ_TARGETS[@]} +do + $CC $CFLAGS -O0 \ + -DFUZZ_TARGET=$FUZZ_TARGET \ + $SRC/target.c -o $OUT/$FUZZ_TARGET +done diff --git a/projects/firefox/project.yaml b/projects/firefox/project.yaml new file mode 100644 index 000000000..ec31b5865 --- /dev/null +++ b/projects/firefox/project.yaml @@ -0,0 +1,8 @@ +homepage: "https://www.mozilla.org/firefox/" +primary_contact: "agaynor@mozilla.com" +fuzzing_engines: +- libfuzzer +sanitizers: +- address +auto_ccs: +- choller@mozilla.com diff --git a/projects/firefox/target.c b/projects/firefox/target.c new file mode 100644 index 000000000..1c225657d --- /dev/null +++ b/projects/firefox/target.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +#define STRINGLIT(S) #S +#define STRINGIFY(S) STRINGLIT(S) + +// Required for oss-fuzz to consider the binary a target. +static const char* magic = "LLVMFuzzerTestOneInput"; + +int main(int argc, char* argv[]) { + char path[PATH_MAX] = {0}; + + if (**argv != '/') { + if (!getcwd(path, PATH_MAX)) { + perror("Couldn't get CWD"); + exit(1); + } + strcat(path, "/"); + } + + if (strlen(path) + strlen(*argv) + 20 > PATH_MAX) { + fprintf(stderr, "Path length would exceed PATH_MAX\n"); + exit(1); + } + + strcat(path, *argv); + + char* solidus = strrchr(path, '/'); + *solidus = 0; // terminate string before last / + + char ld_path[PATH_MAX] = {0}; + strcpy(ld_path, path); + strcat(ld_path, "/lib"); + + char ff_path[PATH_MAX] = {0}; + strcpy(ff_path, path); + strcat(ff_path, "/firefox/firefox"); + + if (getenv("LD_LIBRARY_PATH")) { + // Shouldn't be set. Code can be changed to append if it ever is. + fprintf(stderr, "LD_LIBRARY_PATH unexpectedly set\n"); + exit(1); + } + if (setenv("LD_LIBRARY_PATH", ld_path, 0)) { + perror("Error setting LD_LIBRARY_PATH"); + exit(1); + } + + if (setenv("MOZ_RUN_GTEST", "1", 1) || setenv("LIBFUZZER", "1", 1) || + setenv("FUZZER", STRINGIFY(FUZZ_TARGET), 1)) { + perror("Error setting fuzzing variables"); + exit(1); + } + + // Temporary (or permanent?) work-around for a bug in the fuzzing interface. + // https://bugzilla.mozilla.org/show_bug.cgi?id=1466021#c9 + char* options = getenv("ASAN_OPTIONS"); + if (!options) { + fprintf(stderr, "ASAN_OPTIONS not set ?!\n"); + exit(1); + } + char append[] = ":detect_stack_use_after_return=0"; + char* new_options = (char*)malloc(strlen(options) + sizeof(append)); + memcpy(new_options, options, strlen(options)); + memcpy(new_options + strlen(options), append, sizeof(append)); + if (setenv("ASAN_OPTIONS", new_options, 1)) { + perror("Error setting ASAN_OPTIONS"); + exit(1); + } + free(new_options); + + return execv(ff_path, argv); +}