Add a --fuzz option
authorFabian Keil <fk@fabiankeil.de>
Sat, 24 Dec 2016 16:00:49 +0000 (16:00 +0000)
committerFabian Keil <fk@fabiankeil.de>
Sat, 24 Dec 2016 16:00:49 +0000 (16:00 +0000)
... which exposes Privoxy internals to input from files or stdout.

Mainly tested with American Fuzzy Lop. For details see:
https://www.fabiankeil.de/talks/fuzzing-on-freebsd/

This work was partially funded with donations and done
as part of the Privoxy month in 2015.

Hohoho.

21 files changed:
GNUmakefile.in
actions.c
actions.h
configure.in
errlog.c
errlog.h
filters.c
filters.h
fuzz.c [new file with mode: 0644]
gateway.c
gateway.h
jbsockets.c
jcc.c
jcc.h
loaders.c
loaders.h
parsers.c
pcrs.c
pcrs.h
project.h
urlmatch.c

index 99be236..bb3ea21 100644 (file)
@@ -1,6 +1,6 @@
 # Note:  GNUmakefile is built automatically from GNUmakefile.in
 #
 # Note:  GNUmakefile is built automatically from GNUmakefile.in
 #
-# $Id: GNUmakefile.in,v 1.251 2016/05/08 12:46:15 fabiankeil Exp $
+# $Id: GNUmakefile.in,v 1.252 2016/07/28 08:16:04 fabiankeil Exp $
 #
 # Written by and Copyright (C) 2001-2016 members of the
 # Privoxy team. https://www.privoxy.org/
 #
 # Written by and Copyright (C) 2001-2016 members of the
 # Privoxy team. https://www.privoxy.org/
@@ -197,6 +197,9 @@ C_HDRS = $(C_SRC:.c=.h) project.h actionlist.h
 CLIENT_TAG_SRC = @FEATURE_CLIENT_TAGS_ONLY@client-tags.c
 CLIENT_TAG_OBJS = @FEATURE_CLIENT_TAGS_ONLY@client-tags.@OBJEXT@
 
 CLIENT_TAG_SRC = @FEATURE_CLIENT_TAGS_ONLY@client-tags.c
 CLIENT_TAG_OBJS = @FEATURE_CLIENT_TAGS_ONLY@client-tags.@OBJEXT@
 
+FUZZ_SRC = @FUZZ_ONLY@fuzz.c
+FUZZ_OBJS = @FUZZ_ONLY@fuzz.@OBJEXT@
+
 W32_SRC   = @WIN_ONLY@w32log.c w32taskbar.c win32.c w32svrapi.c
 W32_FILES = @WIN_ONLY@w32.res
 W32_OBJS  = @WIN_ONLY@$(W32_SRC:.c=.@OBJEXT@) $(W32_FILES)
 W32_SRC   = @WIN_ONLY@w32log.c w32taskbar.c win32.c w32svrapi.c
 W32_FILES = @WIN_ONLY@w32.res
 W32_OBJS  = @WIN_ONLY@$(W32_SRC:.c=.@OBJEXT@) $(W32_FILES)
@@ -228,8 +231,8 @@ SOCKET_LIB   = @SOCKET_LIB@
 # PThreads library, if needed.
 PTHREAD_LIB  = @PTHREAD_ONLY@@PTHREAD_LIB@
 
 # PThreads library, if needed.
 PTHREAD_LIB  = @PTHREAD_ONLY@@PTHREAD_LIB@
 
-SRCS         = $(C_SRC) $(CLIENT_TAG_SRC) $(W32_SRC)  $(PCRS_SRC)  $(PCRE_SRC)  $(REGEX_SRC)
-OBJS         = $(C_OBJS) $(CLIENT_TAG_OBJS) $(W32_OBJS) $(PCRS_OBJS) $(PCRE_OBJS) $(REGEX_OBJS)
+SRCS         = $(C_SRC) $(CLIENT_TAG_SRC) $(FUZZ_SRC) $(W32_SRC)  $(PCRS_SRC)  $(PCRE_SRC)  $(REGEX_SRC)
+OBJS         = $(C_OBJS) $(CLIENT_TAG_OBJS) $(FUZZ_OBJS) $(W32_OBJS) $(PCRS_OBJS) $(PCRE_OBJS) $(REGEX_OBJS)
 HDRS         = $(C_HDRS) $(W32_HDRS) $(PCRS_HDRS) $(PCRE_OBJS) $(REGEX_HDRS)
 LIBS         = @LIBS@ $(W32_LIB) $(SOCKET_LIB) $(PTHREAD_LIB)
 
 HDRS         = $(C_HDRS) $(W32_HDRS) $(PCRS_HDRS) $(PCRE_OBJS) $(REGEX_HDRS)
 LIBS         = @LIBS@ $(W32_LIB) $(SOCKET_LIB) $(PTHREAD_LIB)
 
index 90b7bde..7320850 100644 (file)
--- a/actions.c
+++ b/actions.c
@@ -1,4 +1,4 @@
-const char actions_rcs[] = "$Id: actions.c,v 1.96 2016/02/26 12:29:38 fabiankeil Exp $";
+const char actions_rcs[] = "$Id: actions.c,v 1.97 2016/05/03 13:20:37 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/actions.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/actions.c,v $
@@ -124,7 +124,10 @@ static const struct action_name action_names[] =
 };
 
 
 };
 
 
-static int load_one_actions_file(struct client_state *csp, int fileid);
+#ifndef FUZZ
+static
+#endif
+int load_one_actions_file(struct client_state *csp, int fileid);
 
 
 /*********************************************************************
 
 
 /*********************************************************************
@@ -1210,7 +1213,10 @@ static int action_spec_is_valid(struct client_state *csp, const struct action_sp
  * Returns     :  0 => Ok, everything else is an error.
  *
  *********************************************************************/
  * Returns     :  0 => Ok, everything else is an error.
  *
  *********************************************************************/
-static int load_one_actions_file(struct client_state *csp, int fileid)
+#ifndef FUZZ
+static
+#endif
+int load_one_actions_file(struct client_state *csp, int fileid)
 {
 
    /*
 {
 
    /*
index 0f80fe4..7a9cd2e 100644 (file)
--- a/actions.h
+++ b/actions.h
@@ -1,6 +1,6 @@
 #ifndef ACTIONS_H_INCLUDED
 #define ACTIONS_H_INCLUDED
 #ifndef ACTIONS_H_INCLUDED
 #define ACTIONS_H_INCLUDED
-#define ACTIONS_H_VERSION "$Id: actions.h,v 1.23 2013/11/24 14:24:17 fabiankeil Exp $"
+#define ACTIONS_H_VERSION "$Id: actions.h,v 1.24 2013/11/24 14:27:27 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/actions.h,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/actions.h,v $
@@ -78,6 +78,9 @@ extern char * actions_to_line_of_text(const struct current_action_spec *action);
 extern jb_err get_action_token(char **line, char **name, char **value);
 extern void unload_actions_file(void *file_data);
 extern int load_action_files(struct client_state *csp);
 extern jb_err get_action_token(char **line, char **name, char **value);
 extern void unload_actions_file(void *file_data);
 extern int load_action_files(struct client_state *csp);
+#ifdef FUZZ
+extern int load_one_actions_file(struct client_state *csp, int fileid);
+#endif
 
 #ifdef FEATURE_GRACEFUL_TERMINATION
 void unload_current_actions_file(void);
 
 #ifdef FEATURE_GRACEFUL_TERMINATION
 void unload_current_actions_file(void);
index d387194..657edef 100644 (file)
@@ -1,6 +1,6 @@
 dnl Process this file with autoconf to produce a configure script.
 dnl
 dnl Process this file with autoconf to produce a configure script.
 dnl
-dnl $Id: configure.in,v 1.205 2016/09/24 16:16:55 ler762 Exp $
+dnl $Id: configure.in,v 1.206 2016/09/27 22:48:28 ler762 Exp $
 dnl
 dnl Written by and Copyright (C) 2001-2016 the
 dnl Privoxy team. https://www.privoxy.org/
 dnl
 dnl Written by and Copyright (C) 2001-2016 the
 dnl Privoxy team. https://www.privoxy.org/
@@ -32,7 +32,7 @@ dnl =================================================================
 dnl AutoConf Initialization
 dnl =================================================================
 
 dnl AutoConf Initialization
 dnl =================================================================
 
-AC_REVISION($Revision: 1.205 $)
+AC_REVISION($Revision: 1.206 $)
 AC_INIT(jcc.c)
 
 if test ! -f config.h.in; then
 AC_INIT(jcc.c)
 
 if test ! -f config.h.in; then
@@ -1027,6 +1027,16 @@ else
 fi
 AC_SUBST(FEATURE_CLIENT_TAGS_ONLY)
 
 fi
 AC_SUBST(FEATURE_CLIENT_TAGS_ONLY)
 
+AC_ARG_ENABLE(fuzz,
+[  --enable-fuzz                Enable code that makes fuzzing more convenient],
+[if test $enableval = yes; then
+  FUZZ_ONLY=""
+  AC_DEFINE(FUZZ,1,[Define to make fuzzing more convenient.])
+ else
+  FUZZ_ONLY="#"
+fi])
+AC_SUBST(FUZZ_ONLY)
+
 dnl pcre/pcrs is needed for CGI anyway, so
 dnl the choice is only between static and
 dnl dynamic:
 dnl pcre/pcrs is needed for CGI anyway, so
 dnl the choice is only between static and
 dnl dynamic:
index 927289f..05b7dd8 100644 (file)
--- a/errlog.c
+++ b/errlog.c
@@ -1,4 +1,4 @@
-const char errlog_rcs[] = "$Id: errlog.c,v 1.125 2016/01/26 17:12:14 diem Exp $";
+const char errlog_rcs[] = "$Id: errlog.c,v 1.126 2016/02/26 12:29:38 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/errlog.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/errlog.c,v $
@@ -245,6 +245,17 @@ void init_log_module(void)
  *********************************************************************/
 void set_debug_level(int debug_level)
 {
  *********************************************************************/
 void set_debug_level(int debug_level)
 {
+#ifdef FUZZ
+   if (LOG_LEVEL_STFU == debug_level)
+   {
+      debug = LOG_LEVEL_STFU;
+   }
+   if (LOG_LEVEL_STFU == debug)
+   {
+      return;
+   }
+#endif
+
    debug = debug_level | LOG_LEVEL_MINIMUM;
 }
 
    debug = debug_level | LOG_LEVEL_MINIMUM;
 }
 
@@ -700,6 +711,12 @@ void log_error(int loglevel, const char *fmt, ...)
 #endif
       )
    {
 #endif
       )
    {
+#ifdef FUZZ
+      if (debug == LOG_LEVEL_STFU)
+      {
+         return;
+      }
+#endif
       if (loglevel == LOG_LEVEL_FATAL)
       {
          fatal_error("Fatal error. You're not supposed to"
       if (loglevel == LOG_LEVEL_FATAL)
       {
          fatal_error("Fatal error. You're not supposed to"
index f2934ae..05f5190 100644 (file)
--- a/errlog.h
+++ b/errlog.h
@@ -1,6 +1,6 @@
 #ifndef ERRLOG_H_INCLUDED
 #define ERRLOG_H_INCLUDED
 #ifndef ERRLOG_H_INCLUDED
 #define ERRLOG_H_INCLUDED
-#define ERRLOG_H_VERSION "$Id: errlog.h,v 1.29 2012/07/27 17:39:57 fabiankeil Exp $"
+#define ERRLOG_H_VERSION "$Id: errlog.h,v 1.30 2013/11/24 14:23:28 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/errlog.h,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/errlog.h,v $
 #define LOG_LEVEL_CGI        0x0800 /* CGI / templates */
 #define LOG_LEVEL_RECEIVED   0x8000
 #define LOG_LEVEL_ACTIONS   0x10000
 #define LOG_LEVEL_CGI        0x0800 /* CGI / templates */
 #define LOG_LEVEL_RECEIVED   0x8000
 #define LOG_LEVEL_ACTIONS   0x10000
+#ifdef FUZZ
+/*
+ * Permanently disables logging through log_error().
+ * Useful to reduce pointless overhead when fuzzing
+ * without watching stdout.
+ */
+#define LOG_LEVEL_STFU      0x20000
+#endif
 
 /* Following are always on: */
 #define LOG_LEVEL_INFO    0x1000
 
 /* Following are always on: */
 #define LOG_LEVEL_INFO    0x1000
index d4cec75..0450f5a 100644 (file)
--- a/filters.c
+++ b/filters.c
@@ -1,4 +1,4 @@
-const char filters_rcs[] = "$Id: filters.c,v 1.201 2016/03/17 10:40:53 fabiankeil Exp $";
+const char filters_rcs[] = "$Id: filters.c,v 1.202 2016/05/25 10:50:55 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/filters.c,v $
@@ -82,7 +82,6 @@ const char filters_h_rcs[] = FILTERS_H_VERSION;
 
 typedef char *(*filter_function_ptr)();
 static filter_function_ptr get_filter_function(const struct client_state *csp);
 
 typedef char *(*filter_function_ptr)();
 static filter_function_ptr get_filter_function(const struct client_state *csp);
-static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
 static jb_err prepare_for_filtering(struct client_state *csp);
 static void apply_url_actions(struct current_action_spec *action,
                               struct http_request *http,
 static jb_err prepare_for_filtering(struct client_state *csp);
 static void apply_url_actions(struct current_action_spec *action,
                               struct http_request *http,
@@ -1957,7 +1956,11 @@ static char *execute_external_filter(const struct client_state *csp,
  *                or NULL in case something went wrong.
  *
  *********************************************************************/
  *                or NULL in case something went wrong.
  *
  *********************************************************************/
+#ifdef FUZZ
+char *gif_deanimate_response(struct client_state *csp)
+#else
 static char *gif_deanimate_response(struct client_state *csp)
 static char *gif_deanimate_response(struct client_state *csp)
+#endif
 {
    struct binbuffer *in, *out;
    char *p;
 {
    struct binbuffer *in, *out;
    char *p;
@@ -2055,13 +2058,23 @@ static filter_function_ptr get_filter_function(const struct client_state *csp)
  *                JB_ERR_PARSE otherwise
  *
  *********************************************************************/
  *                JB_ERR_PARSE otherwise
  *
  *********************************************************************/
+#ifdef FUZZ
+extern jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
+#else
 static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
 static jb_err remove_chunked_transfer_coding(char *buffer, size_t *size)
+#endif
 {
    size_t newsize = 0;
    unsigned int chunksize = 0;
    char *from_p, *to_p;
    const char *end_of_buffer = buffer + *size;
 
 {
    size_t newsize = 0;
    unsigned int chunksize = 0;
    char *from_p, *to_p;
    const char *end_of_buffer = buffer + *size;
 
+   if (*size == 0)
+   {
+      log_error(LOG_LEVEL_FATAL, "Invalid chunked input. Buffer is empty.");
+      return JB_ERR_PARSE;
+   }
+
    assert(buffer);
    from_p = to_p = buffer;
 
    assert(buffer);
    from_p = to_p = buffer;
 
index 802a3e1..e64e5d0 100644 (file)
--- a/filters.h
+++ b/filters.h
@@ -1,6 +1,6 @@
 #ifndef FILTERS_H_INCLUDED
 #define FILTERS_H_INCLUDED
 #ifndef FILTERS_H_INCLUDED
 #define FILTERS_H_INCLUDED
-#define FILTERS_H_VERSION "$Id: filters.h,v 1.46 2013/12/24 13:32:51 fabiankeil Exp $"
+#define FILTERS_H_VERSION "$Id: filters.h,v 1.47 2016/03/17 10:40:53 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/filters.h,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/filters.h,v $
@@ -111,6 +111,11 @@ extern struct http_response *direct_response(struct client_state *csp);
 extern const char filters_rcs[];
 extern const char filters_h_rcs[];
 
 extern const char filters_rcs[];
 extern const char filters_h_rcs[];
 
+#ifdef FUZZ
+extern char *gif_deanimate_response(struct client_state *csp);
+extern jb_err remove_chunked_transfer_coding(char *buffer, size_t *size);
+#endif
+
 #endif /* ndef FILTERS_H_INCLUDED */
 
 /*
 #endif /* ndef FILTERS_H_INCLUDED */
 
 /*
diff --git a/fuzz.c b/fuzz.c
new file mode 100644 (file)
index 0000000..0e59a80
--- /dev/null
+++ b/fuzz.c
@@ -0,0 +1,628 @@
+/*********************************************************************
+ *
+ * File        :  $Source:$
+ *
+ * Purpose     :  Fuzz-related functions for Privoxy.
+ *
+ * Copyright   :  Written by and Copyright (C) 2014-16 by
+ *                Fabian Keil <fk@fabiankeil.de>
+ *
+ *                This program 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 2 of the License, or (at
+ *                your option) any later version.
+ *
+ *                This program 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.
+ *
+ *                The GNU General Public License should be included with
+ *                this file.  If not, you can view it at
+ *                http://www.gnu.org/copyleft/gpl.html
+ *                or write to the Free Software Foundation, Inc., 59
+ *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ *********************************************************************/
+
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "project.h"
+#include "filters.h"
+#include "loaders.h"
+#include "parsers.h"
+#include "miscutil.h"
+#include "errlog.h"
+#include "actions.h"
+#include "cgi.h"
+#include "loadcfg.h"
+#include "urlmatch.h"
+#include "filters.h"
+#include "jbsockets.h"
+#include "gateway.h"
+#include "jcc.h"
+#include "list.h"
+
+
+#ifdef FUZZ
+static int fuzz_action(struct client_state *csp, char *fuzz_input_file);
+static int fuzz_client_header(struct client_state *csp, char *fuzz_input_file);
+static int fuzz_deflate(struct client_state *csp, char *fuzz_input_file);
+static int fuzz_filter(struct client_state *csp, char *fuzz_input_file);
+static int fuzz_gif(struct client_state *csp, char *fuzz_input_file);
+static int fuzz_gzip(struct client_state *csp, char *fuzz_input_file);
+#ifdef FUZZ_SOCKS
+static int fuzz_socks(struct client_state *csp, char *fuzz_input_file);
+#endif
+static int fuzz_pcrs_substitute(struct client_state *csp, char *fuzz_input_file);
+static int fuzz_server_header(struct client_state *csp, char *fuzz_input_file);
+
+struct fuzz_mode
+{
+   const char *name;
+   const char *expected_input;
+   const int stdin_support;
+   int (* const handler)(struct client_state *csp, char *input_file);
+};
+
+static const struct fuzz_mode fuzz_modes[] = {
+   { "action", "Text to parse as action file.", 0, fuzz_action },
+   { "client-request", "Client request to parse. Currently incomplete", 1, fuzz_client_request },
+   { "client-header", "Client header to parse.", 1, fuzz_client_header },
+   { "chunked-transfer-encoding", "Chunk-encoded data to dechunk.", 1, fuzz_chunked_transfer_encoding },
+   { "deflate", "deflate-compressed data to decompress.", 1, fuzz_deflate },
+   { "filter", "Text to parse as filter file.", 0, fuzz_filter },
+   { "gif", "gif to deanimate.", 1, fuzz_gif },
+   { "gzip", "gzip-compressed data to decompress.", 1, fuzz_gzip },
+   { "pcrs-substitute", "A pcrs-substitute to compile. Not a whole pcrs job! Example: Bla $1 bla \x43 $3 blah.", 1, fuzz_pcrs_substitute },
+   { "server-header", "Server header to parse.", 1, fuzz_server_header },
+   { "server-response", "Server response to parse.", 1, fuzz_server_response },
+#ifdef FUZZ_SOCKS
+   { "socks", "A socks server response. Only reads from stdin!", 1, fuzz_socks },
+#endif
+};
+
+/*********************************************************************
+ *
+ * Function    :  load_fuzz_input_from_stdin
+ *
+ * Description :  Loads stdin into a buffer.
+ *
+ * Parameters  :
+ *          1  :  csp = Used to store the data.
+ *
+ * Returns     :  JB_ERR_OK in case of success,
+ *
+ *********************************************************************/
+static jb_err load_fuzz_input_from_stdin(struct client_state *csp)
+{
+   static char buf[BUFFER_SIZE];
+   int ret;
+
+   while (0 < (ret = read_socket(0, buf, sizeof(buf))))
+   {
+      log_error(LOG_LEVEL_INFO,
+         "Got %d bytes from stdin: %E. They look like this: %N",
+         ret, ret, buf);
+
+      if (add_to_iob(csp->iob, csp->config->buffer_limit, buf, ret))
+      {
+         log_error(LOG_LEVEL_FATAL, "Failed to buffer them.");
+      }
+   }
+
+   log_error(LOG_LEVEL_INFO, "Read %d bytes from stdin",
+      csp->iob->eod -csp->iob->cur);
+
+   return JB_ERR_OK;
+}
+
+/*********************************************************************
+ *
+ * Function    :  load_fuzz_input_from_file
+ *
+ * Description :  Loads file content into a buffer.
+ *
+ * Parameters  :
+ *          1  :  csp = Used to store the data.
+ *          2  :  filename = Name of the file to be loaded.
+ *
+ * Returns     :  JB_ERR_OK in case of success,
+ *
+ *********************************************************************/
+static jb_err load_fuzz_input_from_file(struct client_state *csp, const char *filename)
+{
+   FILE *fp;
+   size_t length;
+   long ret;
+
+   fp = fopen(filename, "rb");
+   if (NULL == fp)
+   {
+      log_error(LOG_LEVEL_FATAL, "Failed to open %s: %E", filename);
+   }
+
+   /* Get file length */
+   if (fseek(fp, 0, SEEK_END))
+   {
+      log_error(LOG_LEVEL_FATAL,
+         "Unexpected error while fseek()ing to the end of %s: %E",
+         filename);
+   }
+   ret = ftell(fp);
+   if (-1 == ret)
+   {
+      log_error(LOG_LEVEL_FATAL,
+         "Unexpected ftell() error while loading %s: %E",
+         filename);
+   }
+   length = (size_t)ret;
+
+   /* Go back to the beginning. */
+   if (fseek(fp, 0, SEEK_SET))
+   {
+      log_error(LOG_LEVEL_FATAL,
+         "Unexpected error while fseek()ing to the beginning of %s: %E",
+         filename);
+   }
+
+   csp->iob->size = length + 1;
+
+   csp->iob->buf = malloc_or_die(csp->iob->size);
+   csp->iob->cur = csp->iob->buf;
+   csp->iob->eod = csp->iob->buf + length;
+
+   if (1 != fread(csp->iob->cur, length, 1, fp))
+   {
+      /*
+       * May theoretically happen if the file size changes between
+       * fseek() and fread() because it's edited in-place. Privoxy
+       * and common text editors don't do that, thus we just fail.
+       */
+      log_error(LOG_LEVEL_FATAL,
+         "Couldn't completely read file %s.", filename);
+   }
+   *csp->iob->eod = '\0';
+
+   fclose(fp);
+
+   return JB_ERR_OK;
+
+}
+
+/*********************************************************************
+ *
+ * Function    :  load_fuzz_input
+ *
+ * Description :  Loads a file into a buffer. XXX: Reverse argument order
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  filename = Name of the file to be loaded.
+ *
+ * Returns     :  JB_ERR_OK in case of success,
+ *
+ *********************************************************************/
+jb_err load_fuzz_input(struct client_state *csp, const char *filename)
+{
+   if (strcmp(filename, "-") == 0)
+   {
+      return load_fuzz_input_from_stdin(csp);
+   }
+
+   return load_fuzz_input_from_file(csp, filename);
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  remove_forbidden_bytes
+ *
+ * Description :  Sanitizes fuzzed data to decrease the likelihood of
+ *                premature parse abortions.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *
+ * Returns     :  N/A
+ *
+ *********************************************************************/
+static void remove_forbidden_bytes(struct client_state *csp)
+{
+   char *p = csp->iob->cur;
+   char first_valid_byte = ' ';
+
+   while (p < csp->iob->eod)
+   {
+      if (*p != '\0')
+      {
+         first_valid_byte = *p;
+         break;
+      }
+      p++;
+   }
+
+   p = csp->iob->cur;
+   while (p < csp->iob->eod)
+   {
+      if (*p == '\0')
+      {
+         *p = first_valid_byte;
+      }
+      p++;
+   }
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  fuzz_action
+ *
+ * Description :  Treat the fuzzed input as action file.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of fuzzed function
+ *
+ *********************************************************************/
+int fuzz_action(struct client_state *csp, char *fuzz_input_file)
+{
+   csp->config->actions_file[0] = fuzz_input_file;
+
+   return(load_action_files(csp));
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  fuzz_client_header
+ *
+ * Description :  Treat the fuzzed input as a client header.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of fuzzed function
+ *
+ *********************************************************************/
+int fuzz_client_header(struct client_state *csp, char *fuzz_input_file)
+{
+   char *header;
+
+   header = get_header(csp->iob);
+
+   if (NULL == header)
+   {
+      return 1;
+   }
+   if (JB_ERR_OK != enlist(csp->headers, header))
+   {
+      return 1;
+   }
+
+   /*
+    * Silence an insightful client_host_adder() warning
+    * about ignored weirdness.
+    */
+   csp->flags |= CSP_FLAG_HOST_HEADER_IS_SET;
+   /* Adding headers doesn't depend on the fuzzed input */
+   csp->flags |= CSP_FLAG_CLIENT_CONNECTION_HEADER_SET;
+
+   /* +hide-if-modified-since{+60} */
+   csp->action->flags |= ACTION_HIDE_IF_MODIFIED_SINCE;
+   csp->action->string[ACTION_STRING_IF_MODIFIED_SINCE] = "+60";
+
+   /* XXX: Enable more actions. */
+
+   return(sed(csp, FILTER_CLIENT_HEADERS));
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  fuzz_filter
+ *
+ * Description :  Treat the fuzzed input as filter file.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of fuzzed function
+ *
+ *********************************************************************/
+int fuzz_filter(struct client_state *csp, char *fuzz_input_file)
+{
+   csp->config->re_filterfile[0] = fuzz_input_file;
+   return (load_one_re_filterfile(csp, 0));
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  fuzz_deflate
+ *
+ * Description :  Treat the fuzzed input as data to deflate.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of fuzzed function
+ *
+ *********************************************************************/
+static int fuzz_deflate(struct client_state *csp, char *fuzz_input_file)
+{
+   csp->content_type = CT_DEFLATE;
+   return(JB_ERR_OK == decompress_iob(csp));
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  fuzz_gif
+ *
+ * Description :  Treat the fuzzed input as a gif to deanimate.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of fuzzed function
+ *
+ *********************************************************************/
+static int fuzz_gif(struct client_state *csp, char *fuzz_input_file)
+{
+   char *deanimated_gif;
+
+   if (6 < csp->iob->size)
+   {
+      /* Why yes of course, officer, this is a gif. */
+      memcpy(csp->iob->cur, "GIF87a", 6);
+   }
+
+   /* Using the last image requires parsing of all images */
+   csp->action->string[ACTION_STRING_DEANIMATE] = "last";
+   deanimated_gif = gif_deanimate_response(csp);
+   if (NULL != deanimated_gif)
+   {
+      free(deanimated_gif);
+      return 0;
+   }
+
+   return 1;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  fuzz_gzip
+ *
+ * Description :  Treat the fuzzed input as data to unzip
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of fuzzed function
+ *
+ *********************************************************************/
+static int fuzz_gzip(struct client_state *csp, char *fuzz_input_file)
+{
+   csp->content_type = CT_GZIP;
+
+   return(JB_ERR_OK == decompress_iob(csp));
+
+}
+
+
+#ifdef FUZZ_SOCKS
+/*********************************************************************
+ *
+ * Function    :  fuzz_socks
+ *
+ * Description :  Treat the fuzzed input as a socks response.
+ *                XXX: This is pretty useless as parsing socks repsonse
+ *                     is trivial.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of fuzzed function
+ *
+ *********************************************************************/
+static int fuzz_socks(struct client_state *csp, char *fuzz_input_file)
+{
+   return(JB_ERR_OK == socks_fuzz(csp));
+}
+#endif
+
+
+/*********************************************************************
+ *
+ * Function    :  fuzz_pcrs_substitute
+ *
+ * Description :  Treat the fuzzed input as a pcrs substitute.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of fuzzed function
+ *
+ *********************************************************************/
+static int fuzz_pcrs_substitute(struct client_state *csp, char *fuzz_input_file)
+{
+   static pcrs_substitute *result;
+   int err;
+
+   remove_forbidden_bytes(csp);
+   result = pcrs_compile_fuzzed_replacement(csp->iob->cur, &err);
+   if (NULL == result)
+   {
+      log_error(LOG_LEVEL_ERROR,
+         "Failed to compile pcrs replacement. Error: %s", pcrs_strerror(err));
+      return 1;
+   }
+   log_error(LOG_LEVEL_INFO, "%s", pcrs_strerror(err));
+   free(result->text);
+   freez(result);
+   return 0;
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  fuzz_server_header
+ *
+ * Description :  Treat the fuzzed input as a server header.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of fuzzed function
+ *
+ *********************************************************************/
+int fuzz_server_header(struct client_state *csp, char *fuzz_input_file)
+{
+   char *header;
+
+   header = get_header(csp->iob);
+
+   if (NULL == header)
+   {
+      return 1;
+   }
+   if (JB_ERR_OK != enlist(csp->headers, header))
+   {
+      return 1;
+   }
+
+   /* Adding headers doesn't depend on the fuzzed input */
+   csp->flags |= CSP_FLAG_CLIENT_HEADER_PARSING_DONE;
+   csp->flags |= CSP_FLAG_SERVER_CONNECTION_HEADER_SET;
+
+   /* +overwrite-last-modified{randomize} */
+   csp->action->flags |= ACTION_OVERWRITE_LAST_MODIFIED;
+   csp->action->string[ACTION_STRING_LAST_MODIFIED] = "randomize";
+
+   /* +limit-cookie-lifetime{60} */
+   csp->action->flags |= ACTION_LIMIT_COOKIE_LIFETIME;
+   csp->action->string[ACTION_STRING_LIMIT_COOKIE_LIFETIME] = "60";
+
+   /* XXX: Enable more actions. */
+
+   return(sed(csp, FILTER_SERVER_HEADERS));
+}
+
+/*********************************************************************
+ *
+ * Function    :  process_fuzzed_input
+ *
+ * Description :  Process the fuzzed input in a specified file treating
+ *                it like the input type specified.
+ *
+ *                XXX: Does not check malloc succcess.
+ *
+ * Parameters  :
+ *          1  :  fuzz_input_type = Type of input.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Return value of the fuzzed function
+ *
+ *********************************************************************/
+int process_fuzzed_input(char *fuzz_input_type, char *fuzz_input_file)
+{
+   static struct client_state csp_stack_storage;
+   static struct configuration_spec config_stack_storage;
+   struct client_state *csp;
+   int i;
+
+   csp = &csp_stack_storage;
+   csp->config = &config_stack_storage;
+   csp->config->buffer_limit = 4096 * 1024;
+   /* In --stfu mode, these will be ignored ... */
+   set_debug_level(LOG_LEVEL_ACTIONS|LOG_LEVEL_CONNECT|LOG_LEVEL_DEANIMATE|LOG_LEVEL_INFO|LOG_LEVEL_ERROR|LOG_LEVEL_RE_FILTER|LOG_LEVEL_HEADER|LOG_LEVEL_WRITING|LOG_LEVEL_RECEIVED);
+
+   csp->flags |= CSP_FLAG_FUZZED_INPUT;
+   csp->config->feature_flags |= RUNTIME_FEATURE_ACCEPT_INTERCEPTED_REQUESTS;
+
+#ifdef FEATURE_CLIENT_TAGS
+   csp->config->trust_x_forwarded_for = 1;
+#endif
+
+   for (i = 0; i < SZ(fuzz_modes); i++)
+   {
+      if (strcmp(fuzz_modes[i].name, fuzz_input_type) == 0)
+      {
+         if (fuzz_modes[i].stdin_support &&
+            (strcmp(fuzz_input_type, "client-request") != 0) &&
+            (strcmp(fuzz_input_type, "server-response") != 0) &&
+            (strcmp(fuzz_input_type, "socks") != 0))
+         {
+            load_fuzz_input(csp, fuzz_input_file);
+         }
+         return (fuzz_modes[i].handler(csp, fuzz_input_file));
+      }
+   }
+
+   log_error(LOG_LEVEL_FATAL,
+      "Unrecognized fuzz type %s for input file %s. You may need --help.",
+      fuzz_input_type, fuzz_input_file);
+
+   /* Not reached. */
+   return 1;
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    :  show_fuzz_usage
+ *
+ * Description :  Shows the --fuzz usage. D'oh.
+ *
+ * Parameters  :  Pointer to argv[0] for identifying ourselves
+ *
+ * Returns     :  void
+ *
+ *********************************************************************/
+void show_fuzz_usage(const char *name)
+{
+   int i;
+
+   printf("%s%s --fuzz fuzz-mode ./path/to/fuzzed/input [--stfu]\n\n",
+      "       ", name);
+
+   printf("Supported fuzz modes and the expected input:\n");
+   for (i = 0; i < SZ(fuzz_modes); i++)
+   {
+      printf(" %s: %s\n", fuzz_modes[i].name, fuzz_modes[i].expected_input);
+   }
+   printf("\n");
+
+   printf("The following fuzz modes read data from stdin if the 'file' is '-'\n");
+   for (i = 0; i < SZ(fuzz_modes); i++)
+   {
+      if (fuzz_modes[i].stdin_support)
+      {
+         printf(" %s\n", fuzz_modes[i].name);
+      }
+   }
+   printf("\n");
+}
+#endif
index 5c2f68e..9bde9e6 100644 (file)
--- a/gateway.c
+++ b/gateway.c
@@ -1,4 +1,4 @@
-const char gateway_rcs[] = "$Id: gateway.c,v 1.98 2016/10/25 10:45:56 fabiankeil Exp $";
+const char gateway_rcs[] = "$Id: gateway.c,v 1.99 2016/10/25 10:46:56 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/gateway.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/gateway.c,v $
@@ -664,6 +664,51 @@ jb_socket forwarded_connect(const struct forward_spec * fwd,
 }
 
 
 }
 
 
+#ifdef FUZZ_SOCKS
+/*********************************************************************
+ *
+ * Function    :  socks_fuzz
+ *
+ * Description :  Wrapper around socks[45]_connect() used for fuzzing.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *
+ * Returns     :  JB_ERR_OK or JB_ERR_PARSE
+ *
+ *********************************************************************/
+extern jb_err socks_fuzz(struct client_state *csp)
+{
+   jb_socket socket;
+   static struct forward_spec fwd;
+   char target_host[] = "fuzz.example.org";
+   int target_port = 12345;
+
+   fwd.gateway_host = strdup_or_die("fuzz.example.org");
+   fwd.gateway_port = 12345;
+
+   fwd.type = SOCKS_4A;
+   socket = socks4_connect(&fwd, target_host, target_port, csp);
+
+   if (JB_INVALID_SOCKET != socket)
+   {
+      fwd.type = SOCKS_5;
+      socket = socks5_connect(&fwd, target_host, target_port, csp);
+   }
+
+   if (JB_INVALID_SOCKET == socket)
+   {
+      log_error(LOG_LEVEL_ERROR, "%s", csp->error_message);
+      return JB_ERR_PARSE;
+   }
+
+   log_error(LOG_LEVEL_INFO, "Input looks like an acceptable socks response");
+
+   return JB_ERR_OK;
+
+}
+#endif
+
 /*********************************************************************
  *
  * Function    :  socks4_connect
 /*********************************************************************
  *
  * Function    :  socks4_connect
@@ -794,6 +839,9 @@ static jb_socket socks4_connect(const struct forward_spec * fwd,
    c->dstip[2]    = (unsigned char)((web_server_addr   >>  8) & 0xff);
    c->dstip[3]    = (unsigned char)((web_server_addr        ) & 0xff);
 
    c->dstip[2]    = (unsigned char)((web_server_addr   >>  8) & 0xff);
    c->dstip[3]    = (unsigned char)((web_server_addr        ) & 0xff);
 
+#ifdef FUZZ_SOCKS
+   sfd = 0;
+#else
    /* pass the request to the socks server */
    sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
 
    /* pass the request to the socks server */
    sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
 
@@ -823,7 +871,9 @@ static jb_socket socks4_connect(const struct forward_spec * fwd,
       err = 1;
       close_socket(sfd);
    }
       err = 1;
       close_socket(sfd);
    }
-   else if (read_socket(sfd, buf, sizeof(buf)) != sizeof(*s))
+   else
+#endif
+       if (read_socket(sfd, buf, sizeof(buf)) != sizeof(*s))
    {
       errstr = "SOCKS4 negotiation read failed.";
       log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
    {
       errstr = "SOCKS4 negotiation read failed.";
       log_error(LOG_LEVEL_CONNECT, "socks4_connect: %s", errstr);
@@ -911,6 +961,7 @@ static const char *translate_socks5_error(int socks_error)
    }
 }
 
    }
 }
 
+
 /*********************************************************************
  *
  * Function    :  socks5_connect
 /*********************************************************************
  *
  * Function    :  socks5_connect
@@ -989,6 +1040,10 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
       return(JB_INVALID_SOCKET);
    }
 
       return(JB_INVALID_SOCKET);
    }
 
+#ifdef FUZZ_SOCKS
+   sfd = 0;
+   if (!err && read_socket(sfd, sbuf, 2) != 2)
+#else
    /* pass the request to the socks server */
    sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
 
    /* pass the request to the socks server */
    sfd = connect_to(fwd->gateway_host, fwd->gateway_port, csp);
 
@@ -1015,7 +1070,6 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
       close_socket(sfd);
       return(JB_INVALID_SOCKET);
    }
       close_socket(sfd);
       return(JB_INVALID_SOCKET);
    }
-
    if (!data_is_available(sfd, csp->config->socket_timeout))
    {
       if (socket_is_still_alive(sfd))
    if (!data_is_available(sfd, csp->config->socket_timeout))
    {
       if (socket_is_still_alive(sfd))
@@ -1030,6 +1084,7 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
    }
 
    if (!err && read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
    }
 
    if (!err && read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
+#endif
    {
       errstr = "SOCKS5 negotiation read failed";
       err = 1;
    {
       errstr = "SOCKS5 negotiation read failed";
       err = 1;
@@ -1076,6 +1131,7 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
    cbuf[client_pos++] = (char)((target_port >> 8) & 0xff);
    cbuf[client_pos++] = (char)((target_port     ) & 0xff);
 
    cbuf[client_pos++] = (char)((target_port >> 8) & 0xff);
    cbuf[client_pos++] = (char)((target_port     ) & 0xff);
 
+#ifndef FUZZ_SOCKS
    if (write_socket(sfd, cbuf, client_pos))
    {
       errstr = "SOCKS5 negotiation write failed";
    if (write_socket(sfd, cbuf, client_pos))
    {
       errstr = "SOCKS5 negotiation write failed";
@@ -1136,6 +1192,7 @@ static jb_socket socks5_connect(const struct forward_spec *fwd,
          clear_iob(csp->client_iob);
       }
    }
          clear_iob(csp->client_iob);
       }
    }
+#endif
 
    server_size = read_socket(sfd, sbuf, SIZE_SOCKS5_REPLY_IPV4);
    if (server_size != SIZE_SOCKS5_REPLY_IPV4)
 
    server_size = read_socket(sfd, sbuf, SIZE_SOCKS5_REPLY_IPV4);
    if (server_size != SIZE_SOCKS5_REPLY_IPV4)
index 5952f01..3e5284b 100644 (file)
--- a/gateway.h
+++ b/gateway.h
@@ -1,6 +1,6 @@
 #ifndef GATEWAY_H_INCLUDED
 #define GATEWAY_H_INCLUDED
 #ifndef GATEWAY_H_INCLUDED
 #define GATEWAY_H_INCLUDED
-#define GATEWAY_H_VERSION "$Id: gateway.h,v 1.22 2012/10/23 10:16:52 fabiankeil Exp $"
+#define GATEWAY_H_VERSION "$Id: gateway.h,v 1.23 2013/11/24 14:23:28 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/gateway.h,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/gateway.h,v $
@@ -66,6 +66,10 @@ extern int connection_destination_matches(const struct reusable_connection *conn
                                           const struct forward_spec *fwd);
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
                                           const struct forward_spec *fwd);
 #endif /* def FEATURE_CONNECTION_KEEP_ALIVE */
 
+#ifdef FUZZ
+extern jb_err socks_fuzz(struct client_state *csp);
+#endif
+
 /*
  * Revision control strings from this header and associated .c file
  */
 /*
  * Revision control strings from this header and associated .c file
  */
index 182382b..187fbc4 100644 (file)
@@ -1,4 +1,4 @@
-const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.137 2016/08/22 14:50:18 fabiankeil Exp $";
+const char jbsockets_rcs[] = "$Id: jbsockets.c,v 1.138 2016/09/27 22:48:28 ler762 Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jbsockets.c,v $
@@ -594,6 +594,14 @@ int write_socket(jb_socket fd, const char *buf, size_t len)
       return 0;
    }
 
       return 0;
    }
 
+#ifdef FUZZ
+   if (!daemon_mode && fd <= 3)
+   {
+      log_error(LOG_LEVEL_WRITING, "Pretending to write to socket %d: %N", fd, len, buf);
+      return 0;
+   }
+#endif
+
    log_error(LOG_LEVEL_WRITING, "to socket %d: %N", fd, len, buf);
 
 #if defined(_WIN32)
    log_error(LOG_LEVEL_WRITING, "to socket %d: %N", fd, len, buf);
 
 #if defined(_WIN32)
diff --git a/jcc.c b/jcc.c
index 408c649..e4ecd01 100644 (file)
--- a/jcc.c
+++ b/jcc.c
@@ -1,4 +1,4 @@
-const char jcc_rcs[] = "$Id: jcc.c,v 1.447 2016/09/27 22:48:28 ler762 Exp $";
+const char jcc_rcs[] = "$Id: jcc.c,v 1.448 2016/12/24 15:58:49 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.c,v $
@@ -178,6 +178,11 @@ static int32 server_thread(void *data);
 #define sleep(N)  DosSleep(((N) * 100))
 #endif
 
 #define sleep(N)  DosSleep(((N) * 100))
 #endif
 
+#ifdef FUZZ
+int process_fuzzed_input(char *fuzz_input_type, char *fuzz_input_file);
+void show_fuzz_usage(const char *name);
+#endif
+
 #ifdef MUTEX_LOCKS_AVAILABLE
 /*
  * XXX: Does the locking stuff really belong in this file?
 #ifdef MUTEX_LOCKS_AVAILABLE
 /*
  * XXX: Does the locking stuff really belong in this file?
@@ -1279,7 +1284,12 @@ static char *get_request_line(struct client_state *csp)
 
    do
    {
 
    do
    {
-      if (!data_is_available(csp->cfd, csp->config->socket_timeout))
+      if (
+#ifdef FUZZ
+          0 == (csp->flags & CSP_FLAG_FUZZED_INPUT) &&
+#endif
+          !data_is_available(csp->cfd, csp->config->socket_timeout)
+          )
       {
          if (socket_is_still_alive(csp->cfd))
          {
       {
          if (socket_is_still_alive(csp->cfd))
          {
@@ -1464,6 +1474,80 @@ static jb_err receive_chunked_client_request_body(struct client_state *csp)
 }
 
 
 }
 
 
+#ifdef FUZZ
+/*********************************************************************
+ *
+ * Function    :  fuzz_chunked_transfer_encoding
+ *
+ * Description :  Treat the fuzzed input as chunked transfer encoding
+ *                to check and dechunk.
+ *
+ * Parameters  :
+ *          1  :  csp      = Used to store the data.
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     : Result of dechunking
+ *
+ *********************************************************************/
+extern int fuzz_chunked_transfer_encoding(struct client_state *csp, char *fuzz_input_file)
+{
+   size_t length;
+   size_t size = (size_t)(csp->iob->eod - csp->iob->cur);
+   enum chunk_status status;
+
+   status = chunked_body_is_complete(csp->iob, &length);
+   if (CHUNK_STATUS_BODY_COMPLETE != status)
+   {
+      log_error(LOG_LEVEL_INFO, "Chunked body is incomplete or invalid");
+   }
+
+   return (JB_ERR_OK == remove_chunked_transfer_coding(csp->iob->cur, &size));
+
+}
+
+
+/*********************************************************************
+ *
+ * Function    : fuzz_client_request
+ *
+ * Description : Try to get a client request from the fuzzed input.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     :  Result of fuzzing.
+ *
+ *********************************************************************/
+extern int fuzz_client_request(struct client_state *csp, char *fuzz_input_file)
+{
+   jb_err err;
+
+   csp->cfd = 0;
+   csp->ip_addr_str = "fuzzer";
+
+   if (strcmp(fuzz_input_file, "-") != 0)
+   {
+      log_error(LOG_LEVEL_FATAL,
+         "Fuzzed client requests can currenty only be read from stdin (-).");
+   }
+   err = receive_client_request(csp);
+   if (err != JB_ERR_OK)
+   {
+      return 1;
+   }
+   err = parse_client_request(csp);
+   if (err != JB_ERR_OK)
+   {
+      return 1;
+   }
+
+   return 0;
+
+}
+#endif  /* def FUZZ */
+
+
 #ifdef FEATURE_FORCE_LOAD
 /*********************************************************************
  *
 #ifdef FEATURE_FORCE_LOAD
 /*********************************************************************
  *
@@ -2539,6 +2623,7 @@ static void handle_established_connection(struct client_state *csp,
    csp->server_connection.timestamp = time(NULL);
 }
 
    csp->server_connection.timestamp = time(NULL);
 }
 
+
 /*********************************************************************
  *
  * Function    :  chat
 /*********************************************************************
  *
  * Function    :  chat
@@ -2828,6 +2913,66 @@ static void chat(struct client_state *csp)
 }
 
 
 }
 
 
+#ifdef FUZZ
+/*********************************************************************
+ *
+ * Function    :  fuzz_server_response
+ *
+ * Description :  Treat the input as a whole server response.
+ *
+ * Parameters  :
+ *          1  :  csp = Current client state (buffers, headers, etc...)
+ *          2  :  fuzz_input_file = File to read the input from.
+ *
+ * Returns     :  0
+ *
+ *********************************************************************/
+extern int fuzz_server_response(struct client_state *csp, char *fuzz_input_file)
+{
+   static struct forward_spec fwd; /* Zero'd due to being static */
+   csp->cfd = 0;
+
+   if (strcmp(fuzz_input_file, "-") == 0)
+   {
+      /* XXX: Doesn'T work yet. */
+      csp->server_connection.sfd = 0;
+   }
+   else
+   {
+      csp->server_connection.sfd = open(fuzz_input_file, O_RDONLY);
+      if (csp->server_connection.sfd == -1)
+      {
+         log_error(LOG_LEVEL_FATAL, "Failed to open %s: %E",
+            fuzz_input_file);
+      }
+   }
+   csp->content_type |= CT_GIF;
+   csp->action->flags |= ACTION_DEANIMATE;
+   csp->action->string[ACTION_STRING_DEANIMATE] = "last";
+
+   csp->http->path = strdup_or_die("/");
+   csp->http->host = strdup_or_die("fuzz.example.org");
+   csp->http->hostport = strdup_or_die("fuzz.example.org:80");
+   /* Prevent client socket monitoring */
+   csp->flags |= CSP_FLAG_PIPELINED_REQUEST_WAITING;
+   csp->flags |= CSP_FLAG_CHUNKED;
+
+   csp->config->feature_flags |= RUNTIME_FEATURE_CONNECTION_KEEP_ALIVE;
+   csp->flags |= CSP_FLAG_CLIENT_CONNECTION_KEEP_ALIVE;
+
+   csp->content_type |= CT_DECLARED|CT_GIF;
+
+   csp->config->socket_timeout = 0;
+
+   cgi_init_error_messages();
+
+   handle_established_connection(csp, &fwd);
+
+   return 0;
+}
+#endif
+
+
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
 /*********************************************************************
  *
 #ifdef FEATURE_CONNECTION_KEEP_ALIVE
 /*********************************************************************
  *
@@ -3158,7 +3303,7 @@ static int32 server_thread(void *data)
  * Returns     :  No. ,-)
  *
  *********************************************************************/
  * Returns     :  No. ,-)
  *
  *********************************************************************/
-static void usage(const char *myname)
+static void usage(const char *name)
 {
    printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n"
           "Usage: %s [--config-test] "
 {
    printf("Privoxy version " VERSION " (" HOME_PAGE_URL ")\n"
           "Usage: %s [--config-test] "
@@ -3169,8 +3314,14 @@ static void usage(const char *myname)
 #if defined(unix)
           "[--no-daemon] [--pidfile pidfile] [--pre-chroot-nslookup hostname] [--user user[.group]] "
 #endif /* defined(unix) */
 #if defined(unix)
           "[--no-daemon] [--pidfile pidfile] [--pre-chroot-nslookup hostname] [--user user[.group]] "
 #endif /* defined(unix) */
-          "[--version] [configfile]\n"
-          "Aborting\n", myname);
+         "[--version] [configfile]\n",
+          name);
+
+#ifdef FUZZ
+   show_fuzz_usage(name);
+#endif
+
+   printf("Aborting\n");
 
    exit(2);
 
 
    exit(2);
 
@@ -3326,7 +3477,6 @@ static void initialize_mutexes(void)
 #endif /* def MUTEX_LOCKS_AVAILABLE */
 }
 
 #endif /* def MUTEX_LOCKS_AVAILABLE */
 }
 
-
 /*********************************************************************
  *
  * Function    :  main
 /*********************************************************************
  *
  * Function    :  main
@@ -3365,6 +3515,10 @@ int main(int argc, char **argv)
    int do_chroot = 0;
    char *pre_chroot_nslookup_to_load_resolver = NULL;
 #endif
    int do_chroot = 0;
    char *pre_chroot_nslookup_to_load_resolver = NULL;
 #endif
+#ifdef FUZZ
+   char *fuzz_input_type = NULL;
+   char *fuzz_input_file = NULL;
+#endif
 
    Argc = argc;
    Argv = argv;
 
    Argc = argc;
    Argv = argv;
@@ -3492,7 +3646,20 @@ int main(int argc, char **argv)
       {
          do_config_test = 1;
       }
       {
          do_config_test = 1;
       }
-
+#ifdef FUZZ
+      else if (strcmp(argv[argc_pos], "--fuzz") == 0)
+      {
+         argc_pos++;
+         if (argc < argc_pos + 2) usage(argv[0]);
+         fuzz_input_type = argv[argc_pos];
+         argc_pos++;
+         fuzz_input_file = argv[argc_pos];
+      }
+      else if (strcmp(argv[argc_pos], "--stfu") == 0)
+      {
+         set_debug_level(LOG_LEVEL_STFU);
+      }
+#endif
       else if (argc_pos + 1 != argc)
       {
          /*
       else if (argc_pos + 1 != argc)
       {
          /*
@@ -3596,6 +3763,13 @@ int main(int argc, char **argv)
 # endif /* def _WIN_CONSOLE */
 #endif /* def _WIN32 */
 
 # endif /* def _WIN_CONSOLE */
 #endif /* def _WIN32 */
 
+#ifdef FUZZ
+   if (fuzz_input_type != NULL)
+   {
+      exit(process_fuzzed_input(fuzz_input_type, fuzz_input_file));
+   }
+#endif
+
    if (do_config_test)
    {
       exit(NULL == load_config());
    if (do_config_test)
    {
       exit(NULL == load_config());
diff --git a/jcc.h b/jcc.h
index 43b765f..083348a 100644 (file)
--- a/jcc.h
+++ b/jcc.h
@@ -1,6 +1,6 @@
 #ifndef JCC_H_INCLUDED
 #define JCC_H_INCLUDED
 #ifndef JCC_H_INCLUDED
 #define JCC_H_INCLUDED
-#define JCC_H_VERSION "$Id: jcc.h,v 1.35 2014/06/02 06:22:21 fabiankeil Exp $"
+#define JCC_H_VERSION "$Id: jcc.h,v 1.36 2016/03/17 10:40:53 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.h,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/jcc.h,v $
@@ -117,6 +117,12 @@ int main(int argc, char **argv);
 extern const char jcc_rcs[];
 extern const char jcc_h_rcs[];
 
 extern const char jcc_rcs[];
 extern const char jcc_h_rcs[];
 
+#ifdef FUZZ
+extern int fuzz_client_request(struct client_state *csp, char *fuzz_input_file);
+extern int fuzz_server_response(struct client_state *csp, char *fuzz_input_file);
+extern int fuzz_chunked_transfer_encoding(struct client_state *csp, char *fuzz_input_file);
+#endif
+
 #endif /* ndef JCC_H_INCLUDED */
 
 /*
 #endif /* ndef JCC_H_INCLUDED */
 
 /*
index 1fdeabf..135b208 100644 (file)
--- a/loaders.c
+++ b/loaders.c
@@ -1,4 +1,4 @@
-const char loaders_rcs[] = "$Id: loaders.c,v 1.104 2016/05/22 12:43:07 fabiankeil Exp $";
+const char loaders_rcs[] = "$Id: loaders.c,v 1.105 2016/05/25 10:50:55 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/loaders.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/loaders.c,v $
@@ -74,7 +74,9 @@ const char loaders_h_rcs[] = LOADERS_H_VERSION;
 static struct file_list *current_trustfile      = NULL;
 #endif /* def FEATURE_TRUST */
 
 static struct file_list *current_trustfile      = NULL;
 #endif /* def FEATURE_TRUST */
 
+#ifndef FUZZ
 static int load_one_re_filterfile(struct client_state *csp, int fileid);
 static int load_one_re_filterfile(struct client_state *csp, int fileid);
+#endif
 
 static struct file_list *current_re_filterfile[MAX_AF_FILES]  = {
    NULL, NULL, NULL, NULL, NULL,
 
 static struct file_list *current_re_filterfile[MAX_AF_FILES]  = {
    NULL, NULL, NULL, NULL, NULL,
@@ -361,6 +363,7 @@ jb_err simple_read_line(FILE *fp, char **dest, int *newline)
    for (;;)
    {
       ch = getc(fp);
    for (;;)
    {
       ch = getc(fp);
+
       if (ch == EOF)
       {
          if (len > 0)
       if (ch == EOF)
       {
          if (len > 0)
@@ -417,6 +420,7 @@ jb_err simple_read_line(FILE *fp, char **dest, int *newline)
       }
       else if (ch == 0)
       {
       }
       else if (ch == 0)
       {
+         /* XXX: Why do we allow this anyway? */
          *p = '\0';
          *dest = buf;
          return JB_ERR_OK;
          *p = '\0';
          *dest = buf;
          return JB_ERR_OK;
index 5e65402..54c4aa2 100644 (file)
--- a/loaders.h
+++ b/loaders.h
@@ -1,6 +1,6 @@
 #ifndef LOADERS_H_INCLUDED
 #define LOADERS_H_INCLUDED
 #ifndef LOADERS_H_INCLUDED
 #define LOADERS_H_INCLUDED
-#define LOADERS_H_VERSION "$Id: loaders.h,v 1.31 2013/11/06 16:27:37 fabiankeil Exp $"
+#define LOADERS_H_VERSION "$Id: loaders.h,v 1.32 2013/11/24 14:23:28 fabiankeil Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/loaders.h,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/loaders.h,v $
@@ -74,6 +74,9 @@ extern jb_err simple_read_line(FILE *fp, char **dest, int *newline);
 extern short int MustReload;
 extern int load_action_files(struct client_state *csp);
 extern int load_re_filterfiles(struct client_state *csp);
 extern short int MustReload;
 extern int load_action_files(struct client_state *csp);
 extern int load_re_filterfiles(struct client_state *csp);
+#ifdef FUZZ
+extern int load_one_re_filterfile(struct client_state *csp, int fileid);
+#endif
 
 #ifdef FEATURE_TRUST
 extern int load_trustfile(struct client_state *csp);
 
 #ifdef FEATURE_TRUST
 extern int load_trustfile(struct client_state *csp);
index b8f636d..57e5cde 100644 (file)
--- a/parsers.c
+++ b/parsers.c
@@ -1,4 +1,4 @@
-const char parsers_rcs[] = "$Id: parsers.c,v 1.309 2016/04/30 10:28:36 fabiankeil Exp $";
+const char parsers_rcs[] = "$Id: parsers.c,v 1.310 2016/12/09 09:13:19 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/parsers.c,v $
@@ -421,8 +421,13 @@ jb_err decompress_iob(struct client_state *csp)
    int status;       /* return status of the inflate() call */
    z_stream zstr;    /* used by calls to zlib */
 
    int status;       /* return status of the inflate() call */
    z_stream zstr;    /* used by calls to zlib */
 
+#ifdef FUZZ
+   assert(csp->iob->cur - csp->iob->buf >= 0);
+   assert(csp->iob->eod - csp->iob->cur >= 0);
+#else
    assert(csp->iob->cur - csp->iob->buf > 0);
    assert(csp->iob->eod - csp->iob->cur > 0);
    assert(csp->iob->cur - csp->iob->buf > 0);
    assert(csp->iob->eod - csp->iob->cur > 0);
+#endif
 
    bufsize = csp->iob->size;
    skip_size = (size_t)(csp->iob->cur - csp->iob->buf);
 
    bufsize = csp->iob->size;
    skip_size = (size_t)(csp->iob->cur - csp->iob->buf);
@@ -718,7 +723,7 @@ jb_err decompress_iob(struct client_state *csp)
     * Make sure the new uncompressed iob obeys some minimal
     * consistency conditions.
     */
     * Make sure the new uncompressed iob obeys some minimal
     * consistency conditions.
     */
-   if ((csp->iob->buf <  csp->iob->cur)
+   if ((csp->iob->buf <=  csp->iob->cur)
     && (csp->iob->cur <= csp->iob->eod)
     && (csp->iob->eod <= csp->iob->buf + csp->iob->size))
    {
     && (csp->iob->cur <= csp->iob->eod)
     && (csp->iob->eod <= csp->iob->buf + csp->iob->size))
    {
diff --git a/pcrs.c b/pcrs.c
index 955ccf8..0553a53 100644 (file)
--- a/pcrs.c
+++ b/pcrs.c
@@ -1,4 +1,4 @@
-const char pcrs_rcs[] = "$Id: pcrs.c,v 1.49 2016/05/08 10:45:51 fabiankeil Exp $";
+const char pcrs_rcs[] = "$Id: pcrs.c,v 1.50 2016/05/25 10:50:28 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/pcrs.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/pcrs.c,v $
@@ -182,6 +182,38 @@ static int pcrs_parse_perl_options(const char *optstring, int *flags)
 }
 
 
 }
 
 
+#ifdef FUZZ
+/*********************************************************************
+ *
+ * Function    :  pcrs_compile_fuzzed_replacement
+ *
+ * Description :  Wrapper around pcrs_compile_replacement() for
+ *                fuzzing purposes.
+ *
+ * Parameters  :
+ *          1  :  replacement = replacement part of s/// operator
+ *                              in perl syntax
+ *          2  :  errptr = pointer to an integer in which error
+ *                         conditions can be returned.
+ *
+ * Returns     :  pcrs_substitute data structure, or NULL if an
+ *                error is encountered. In that case, *errptr has
+ *                the reason.
+ *
+ *********************************************************************/
+extern pcrs_substitute *pcrs_compile_fuzzed_replacement(const char *replacement, int *errptr)
+{
+   int capturecount = PCRS_MAX_SUBMATCHES; /* XXX: fuzzworthy? */
+   int trivial_flag = 0; /* We don't want to fuzz strncpy() */
+
+   *errptr = 0; /* XXX: Should pcrs_compile_replacement() do this? */
+
+   return pcrs_compile_replacement(replacement, trivial_flag, capturecount, errptr);
+
+}
+#endif
+
+
 /*********************************************************************
  *
  * Function    :  pcrs_compile_replacement
 /*********************************************************************
  *
  * Function    :  pcrs_compile_replacement
@@ -212,7 +244,14 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr
    size_t length;
    char *text;
    pcrs_substitute *r;
    size_t length;
    char *text;
    pcrs_substitute *r;
-
+#ifdef FUZZ
+   static const char *replacement_stack;
+   static const size_t *length_stack;
+   static pcrs_substitute *r_stack;
+
+   replacement_stack = replacement;
+   length_stack = &length;
+#endif
    i = k = l = quoted = 0;
 
    /*
    i = k = l = quoted = 0;
 
    /*
@@ -233,6 +272,10 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr
    }
    memset(r, '\0', sizeof(pcrs_substitute));
 
    }
    memset(r, '\0', sizeof(pcrs_substitute));
 
+#ifdef FUZZ
+   r_stack = r;
+#endif
+
    length = strlen(replacement);
 
    if (NULL == (text = (char *)malloc(length + 1)))
    length = strlen(replacement);
 
    if (NULL == (text = (char *)malloc(length + 1)))
diff --git a/pcrs.h b/pcrs.h
index 9798db1..dc9533d 100644 (file)
--- a/pcrs.h
+++ b/pcrs.h
@@ -11,7 +11,7 @@
  *
  *********************************************************************/
 
  *
  *********************************************************************/
 
-#define PCRS_H_VERSION "$Id: pcrs.h,v 1.17 2009/05/16 13:27:20 fabiankeil Exp $"
+#define PCRS_H_VERSION "$Id: pcrs.h,v 1.18 2013/11/24 14:23:28 fabiankeil Exp $"
 
 
 #ifndef _PCRE_H
 
 
 #ifndef _PCRE_H
@@ -133,6 +133,10 @@ extern pcrs_job *pcrs_compile_dynamic_command(char *pcrs_command, const struct p
 #define PCRS_BUFFER_SIZE 4000
 #endif /* ndef PCRS_BUFFER_SIZE */
 
 #define PCRS_BUFFER_SIZE 4000
 #endif /* ndef PCRS_BUFFER_SIZE */
 
+#ifdef FUZZ
+extern pcrs_substitute *pcrs_compile_fuzzed_replacement(const char *replacement, int *errptr);
+#endif
+
 #endif /* ndef PCRS_H_INCLUDED */
 
 /*
 #endif /* ndef PCRS_H_INCLUDED */
 
 /*
index dcd2c31..e7c7323 100644 (file)
--- a/project.h
+++ b/project.h
@@ -1,7 +1,7 @@
 #ifndef PROJECT_H_INCLUDED
 #define PROJECT_H_INCLUDED
 /** Version string. */
 #ifndef PROJECT_H_INCLUDED
 #define PROJECT_H_INCLUDED
 /** Version string. */
-#define PROJECT_H_VERSION "$Id: project.h,v 1.216 2016/05/25 10:50:55 fabiankeil Exp $"
+#define PROJECT_H_VERSION "$Id: project.h,v 1.217 2016/09/27 22:48:28 ler762 Exp $"
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/project.h,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/project.h,v $
@@ -861,6 +861,12 @@ struct reusable_connection
  */
 #define CSP_FLAG_CRUNCHED                           0x04000000U
 
  */
 #define CSP_FLAG_CRUNCHED                           0x04000000U
 
+#ifdef FUZZ
+/**
+ * Flag for csp->flags: Set if we are working with fuzzed input
+ */
+#define CSP_FLAG_FUZZED_INPUT                       0x08000000U
+#endif
 
 /*
  * Flags for use in return codes of child processes
 
 /*
  * Flags for use in return codes of child processes
index 4839c2b..37802c5 100644 (file)
@@ -1,4 +1,4 @@
-const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.87 2016/02/26 12:29:39 fabiankeil Exp $";
+const char urlmatch_rcs[] = "$Id: urlmatch.c,v 1.88 2016/03/17 10:40:53 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/urlmatch.c,v $
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/urlmatch.c,v $
@@ -624,11 +624,11 @@ static jb_err compile_pattern(const char *pattern, enum regex_anchoring anchorin
                               struct pattern_spec *url, regex_t **regex)
 {
    int errcode;
                               struct pattern_spec *url, regex_t **regex)
 {
    int errcode;
-   char rebuf[BUFFER_SIZE];
    const char *fmt = NULL;
    const char *fmt = NULL;
+   char *rebuf;
+   size_t rebuf_size;
 
    assert(pattern);
 
    assert(pattern);
-   assert(strlen(pattern) < sizeof(rebuf) - 2);
 
    if (pattern[0] == '\0')
    {
 
    if (pattern[0] == '\0')
    {
@@ -654,27 +654,30 @@ static jb_err compile_pattern(const char *pattern, enum regex_anchoring anchorin
          log_error(LOG_LEVEL_FATAL,
             "Invalid anchoring in compile_pattern %d", anchoring);
    }
          log_error(LOG_LEVEL_FATAL,
             "Invalid anchoring in compile_pattern %d", anchoring);
    }
-
+   rebuf_size = strlen(pattern) + strlen(fmt);
+   rebuf = malloc_or_die(rebuf_size);
    *regex = zalloc_or_die(sizeof(**regex));
 
    *regex = zalloc_or_die(sizeof(**regex));
 
-   snprintf(rebuf, sizeof(rebuf), fmt, pattern);
+   snprintf(rebuf, rebuf_size, fmt, pattern);
 
    errcode = regcomp(*regex, rebuf, (REG_EXTENDED|REG_NOSUB|REG_ICASE));
 
    if (errcode)
    {
 
    errcode = regcomp(*regex, rebuf, (REG_EXTENDED|REG_NOSUB|REG_ICASE));
 
    if (errcode)
    {
-      size_t errlen = regerror(errcode, *regex, rebuf, sizeof(rebuf));
-      if (errlen > (sizeof(rebuf) - (size_t)1))
+      size_t errlen = regerror(errcode, *regex, rebuf, rebuf_size);
+      if (errlen > (rebuf_size - (size_t)1))
       {
       {
-         errlen = sizeof(rebuf) - (size_t)1;
+         errlen = rebuf_size - (size_t)1;
       }
       rebuf[errlen] = '\0';
       log_error(LOG_LEVEL_ERROR, "error compiling %s from %s: %s",
          pattern, url->spec, rebuf);
       free_pattern_spec(url);
       }
       rebuf[errlen] = '\0';
       log_error(LOG_LEVEL_ERROR, "error compiling %s from %s: %s",
          pattern, url->spec, rebuf);
       free_pattern_spec(url);
+      freez(rebuf);
 
       return JB_ERR_PARSE;
    }
 
       return JB_ERR_PARSE;
    }
+   freez(rebuf);
 
    return JB_ERR_OK;
 
 
    return JB_ERR_OK;