diff --git a/configure.ac b/configure.ac index 5bb366e76..ee9403149 100644 --- a/configure.ac +++ b/configure.ac @@ -991,6 +991,15 @@ AC_CHECK_LIB(util, openpty, ]) AC_SUBST(LIBUTIL_LIBS) +# TODO choose when to build fuzzing with option ? +AC_CHECK_LIB(FuzzingEngine, main, + [ LIB_FUZZING_ENGINE="$LIB_FUZZING_ENGINE" + have_fuzz=yes + ]) +AC_SUBST(LIB_FUZZING_ENGINE) +AC_CHECK_PROG(HAVE_CLANGXX, clang++, 1) +AM_CONDITIONAL(HAVE_LIB_FUZZING_ENGINE, [test "$have_fuzz" = yes -a "$HAVE_CLANGXX" = 1]) + # shred is used to clean temporary plain text files. AC_PATH_PROG(SHRED, shred, /usr/bin/shred) AC_DEFINE_UNQUOTED(SHRED, @@ -2094,6 +2103,7 @@ tests/migrations/Makefile tests/gpgsm/Makefile tests/gpgme/Makefile tests/pkits/Makefile +tests/fuzz/Makefile g10/gpg.w32-manifest ]) diff --git a/g10/Makefile.am b/g10/Makefile.am index 2b92daf33..505d98f5e 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -47,6 +47,7 @@ endif # NB: We use noinst_ for gpg and gpgv so that we can install them with # the install-hook target under the name gpg2/gpgv2. noinst_PROGRAMS = gpg +noinst_LIBRARIES = libgpg.a if !HAVE_W32CE_SYSTEM noinst_PROGRAMS += gpgv endif @@ -164,6 +165,9 @@ gpg_SOURCES = gpg.c \ keyedit.c keyedit.h \ $(gpg_sources) +libgpg_a_SOURCES = keyedit.c keyedit.h \ + $(gpg_sources) + #gpgcompose_SOURCES = gpgcompose.c $(gpg_sources) gpgv_SOURCES = gpgv.c \ $(common_source) \ diff --git a/g10/armor.c b/g10/armor.c index eb2d28bca..594f5bd2d 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -313,7 +313,9 @@ static void invalid_armor(void) { write_status(STATUS_BADARMOR); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION g10_exit(1); /* stop here */ +#endif } diff --git a/g10/call-dirmngr.h b/g10/call-dirmngr.h index 285c4cb4d..7af328c2a 100644 --- a/g10/call-dirmngr.h +++ b/g10/call-dirmngr.h @@ -19,6 +19,8 @@ #ifndef GNUPG_G10_CALL_DIRMNGR_H #define GNUPG_G10_CALL_DIRMNGR_H +#include "options.h" + void gpg_dirmngr_deinit_session_data (ctrl_t ctrl); gpg_error_t gpg_dirmngr_ks_list (ctrl_t ctrl, char **r_keyserver); diff --git a/g10/compress-bz2.c b/g10/compress-bz2.c index 45aa40dfc..1a74a89d7 100644 --- a/g10/compress-bz2.c +++ b/g10/compress-bz2.c @@ -155,8 +155,15 @@ do_uncompress( compress_filter_context_t *zfx, bz_stream *bzs, (unsigned)bzs->avail_in, (unsigned)bzs->avail_out, zrc); if( zrc == BZ_STREAM_END ) rc = -1; /* eof */ - else if( zrc != BZ_OK && zrc != BZ_PARAM_ERROR ) - log_fatal("bz2lib inflate problem: rc=%d\n", zrc ); + else if( zrc != BZ_OK && zrc != BZ_PARAM_ERROR ) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + log_error("bz2lib inflate problem: rc=%d\n", zrc ); + rc = GPG_ERR_BAD_DATA; + break; +#else + log_fatal("bz2lib inflate problem: rc=%d\n", zrc ); +#endif + } else if (zrc == BZ_OK && eofseen && !bzs->avail_in && bzs->avail_out > 0) { diff --git a/g10/compress.c b/g10/compress.c index e7a6f2b11..9a9ab5460 100644 --- a/g10/compress.c +++ b/g10/compress.c @@ -204,10 +204,19 @@ do_uncompress( compress_filter_context_t *zfx, z_stream *zs, if( zrc == Z_STREAM_END ) rc = -1; /* eof */ else if( zrc != Z_OK && zrc != Z_BUF_ERROR ) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + rc = -1; + zrc = Z_BUF_ERROR; + if( zs->msg ) + log_error("zlib inflate problem: %s\n", zs->msg ); + else + log_error("zlib inflate problem: rc=%d\n", zrc ); +#else if( zs->msg ) log_fatal("zlib inflate problem: %s\n", zs->msg ); else log_fatal("zlib inflate problem: rc=%d\n", zrc ); +#endif } } while (zs->avail_out && zrc != Z_STREAM_END && zrc != Z_BUF_ERROR && !leave); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 2f92c1d2c..41e077a6a 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -738,7 +738,12 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, * the uncompressing layer - in some error cases it just loops * and spits out 0xff bytes. */ log_error ("%s: garbled packet detected\n", iobuf_where (inp)); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + rc = gpg_error (GPG_ERR_INV_PACKET); + goto leave; +#else g10_exit (2); +#endif } if (out && pkttype) diff --git a/g10/plaintext.c b/g10/plaintext.c index f9e0a4296..7b9709c08 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -656,10 +656,16 @@ ask_for_detached_datafile (gcry_md_hd_t md, gcry_md_hd_t md2, if (!fp) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + errno = ENOENT; + rc = gpg_error_from_syserror (); + goto leave; +#else if (opt.verbose) log_info (_("reading stdin ...\n")); fp = iobuf_open (NULL); log_assert (fp); +#endif } do_hash (md, md2, fp, textmode); iobuf_close (fp); diff --git a/g10/sig-check.c b/g10/sig-check.c index 4c172d692..bd87f00ad 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -760,8 +760,9 @@ check_revocation_keys (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig) { gcry_md_hd_t md; - if (gcry_md_open (&md, sig->digest_algo, 0)) - BUG (); + rc = gcry_md_open (&md, sig->digest_algo, 0); + if (rc) + return rc; hash_public_key(md,pk); /* Note: check_signature only checks that the signature is good. It does not fail if the key is revoked. */ diff --git a/tests/Makefile.am b/tests/Makefile.am index b9be6aaa6..d6659eaf1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,7 +18,13 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = gpgscm openpgp migrations gpgsm gpgme pkits . +SUBDIRS = gpgscm openpgp migrations gpgsm gpgme pkits + +if MAINTAINER_MODE +SUBDIRS += fuzz +endif + +SUBDIRS += . GPGSM = ../sm/gpgsm diff --git a/tests/fuzz/Makefile.am b/tests/fuzz/Makefile.am new file mode 100644 index 000000000..3bf039a54 --- /dev/null +++ b/tests/fuzz/Makefile.am @@ -0,0 +1,84 @@ +# Makefile.am - For tests/fuzz +# Copyright (C) 2018 Free Software Foundation, Inc. +# +# This file is part of GnuPG. +# +# GnuPG is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GnuPG is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# Process this file with automake to create Makefile.in + + +# Programs required before we can run these tests. +required_pgms = ../../g10/gpg$(EXEEXT) + + +# Force linking with clang++ even if we have pure C fuzzing targets +CCLD = clang++ +AM_LDFLAGS = -stdlib=libc++ + +AM_CPPFLAGS = -I$(top_srcdir)/common -I$(top_srcdir)/g10 +include $(top_srcdir)/am/cmacros.am + +noinst_PROGRAMS = fuzz_verify fuzz_import fuzz_decrypt fuzz_list + +fuzz_verify_SOURCES = fuzz_verify.c + +fuzz_verify_LDADD = $(top_srcdir)/g10/libgpg.a ../../kbx/libkeybox.a ../../common/libcommon.a ../../common/libgpgrl.a $(LIB_FUZZING_ENGINE) \ + $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ + $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBICONV) $(resource_objs) $(extra_sys_libs) + +fuzz_verify_DEPENDENCIES = fuzz_verify_seed_corpus.zip + +fuzz_verify_seed_corpus.zip: + cd .. && zip -r fuzz/fuzz_verify_seed_corpus.zip openpgp/tofu/conflicting/* openpgp/tofu/cross-sigs/* openpgp/samplemsgs/* + +fuzz_import_SOURCES = fuzz_import.c + +fuzz_import_LDADD = $(top_srcdir)/g10/libgpg.a ../../kbx/libkeybox.a ../../common/libcommon.a ../../common/libgpgrl.a $(LIB_FUZZING_ENGINE)\ + $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ + $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBICONV) $(resource_objs) $(extra_sys_libs) + +fuzz_import_DEPENDENCIES = fuzz_import_seed_corpus.zip + +fuzz_import_seed_corpus.zip: + cd .. && zip -r fuzz/fuzz_import_seed_corpus.zip openpgp/samplekeys/* openpgp/key-selection/* openpgp/*.asc openpgp/trust-pgp/*.asc openpgp/tofu/conflicting/* openpgp/tofu/cross-sigs/* + +fuzz_decrypt_SOURCES = fuzz_decrypt.c + +fuzz_decrypt_LDADD = $(top_srcdir)/g10/libgpg.a ../../kbx/libkeybox.a ../../common/libcommon.a ../../common/libgpgrl.a $(LIB_FUZZING_ENGINE)\ + $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ + $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBICONV) $(resource_objs) $(extra_sys_libs) + +fuzz_decrypt_DEPENDENCIES = fuzz_decrypt_seed_corpus.zip + +fuzz_decrypt_seed_corpus.zip: + cd .. && zip -r fuzz/fuzz_decrypt_seed_corpus.zip openpgp/tofu/conflicting/* openpgp/tofu/cross-sigs/* openpgp/samplemsgs/* + +fuzz_list_SOURCES = fuzz_list.c + +fuzz_list_LDADD = $(top_srcdir)/g10/libgpg.a ../../kbx/libkeybox.a ../../common/libcommon.a ../../common/libgpgrl.a $(LIB_FUZZING_ENGINE)\ +$(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ +$(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \ +$(LIBICONV) $(resource_objs) $(extra_sys_libs) + +fuzz_list_DEPENDENCIES = fuzz_list_seed_corpus.zip + +fuzz_list_seed_corpus.zip: + cd .. && zip -r fuzz/fuzz_list_seed_corpus.zip openpgp/ + +# We need to depend on a couple of programs so that the tests don't +# start before all programs are built. +all-local: $(required_pgms)