From: Fabian Keil Date: Sat, 24 Dec 2016 16:00:49 +0000 (+0000) Subject: Add a --fuzz option X-Git-Tag: v_3_0_27~256 X-Git-Url: http://www.privoxy.org/gitweb/?p=privoxy.git;a=commitdiff_plain;h=f67b3326138f428863c21c7738e0c8db87fa6f5c Add a --fuzz option ... 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. --- diff --git a/GNUmakefile.in b/GNUmakefile.in index 99be2368..bb3ea21a 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -1,6 +1,6 @@ # 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/ @@ -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@ +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) @@ -228,8 +231,8 @@ SOCKET_LIB = @SOCKET_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) diff --git a/actions.c b/actions.c index 90b7bde8..7320850b 100644 --- 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 $ @@ -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. * *********************************************************************/ -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) { /* diff --git a/actions.h b/actions.h index 0f80fe44..7a9cd2ec 100644 --- a/actions.h +++ b/actions.h @@ -1,6 +1,6 @@ #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 $ @@ -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); +#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); diff --git a/configure.in b/configure.in index d3871940..657edef4 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,6 @@ 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/ @@ -32,7 +32,7 @@ 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 @@ -1027,6 +1027,16 @@ else 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: diff --git a/errlog.c b/errlog.c index 927289ff..05b7dd8e 100644 --- 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 $ @@ -245,6 +245,17 @@ void init_log_module(void) *********************************************************************/ 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; } @@ -700,6 +711,12 @@ void log_error(int loglevel, const char *fmt, ...) #endif ) { +#ifdef FUZZ + if (debug == LOG_LEVEL_STFU) + { + return; + } +#endif if (loglevel == LOG_LEVEL_FATAL) { fatal_error("Fatal error. You're not supposed to" diff --git a/errlog.h b/errlog.h index f2934ae0..05f51901 100644 --- a/errlog.h +++ b/errlog.h @@ -1,6 +1,6 @@ #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 $ @@ -55,6 +55,14 @@ #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 diff --git a/filters.c b/filters.c index d4cec754..0450f5a7 100644 --- 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 $ @@ -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); -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, @@ -1957,7 +1956,11 @@ static char *execute_external_filter(const struct client_state *csp, * 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) +#endif { 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 * *********************************************************************/ +#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) +#endif { 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; diff --git a/filters.h b/filters.h index 802a3e1d..e64e5d0d 100644 --- a/filters.h +++ b/filters.h @@ -1,6 +1,6 @@ #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 $ @@ -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[]; +#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 */ /* diff --git a/fuzz.c b/fuzz.c new file mode 100644 index 00000000..0e59a80f --- /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 + * + * 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 +#include +#include +#include + +#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 diff --git a/gateway.c b/gateway.c index 5c2f68e8..9bde9e61 100644 --- 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 $ @@ -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 @@ -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); +#ifdef FUZZ_SOCKS + sfd = 0; +#else /* 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); } - 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); @@ -911,6 +961,7 @@ static const char *translate_socks5_error(int socks_error) } } + /********************************************************************* * * Function : socks5_connect @@ -989,6 +1040,10 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, 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); @@ -1015,7 +1070,6 @@ static jb_socket socks5_connect(const struct forward_spec *fwd, close_socket(sfd); return(JB_INVALID_SOCKET); } - 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) +#endif { 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); +#ifndef FUZZ_SOCKS 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); } } +#endif server_size = read_socket(sfd, sbuf, SIZE_SOCKS5_REPLY_IPV4); if (server_size != SIZE_SOCKS5_REPLY_IPV4) diff --git a/gateway.h b/gateway.h index 5952f011..3e5284b9 100644 --- a/gateway.h +++ b/gateway.h @@ -1,6 +1,6 @@ #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 $ @@ -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 */ +#ifdef FUZZ +extern jb_err socks_fuzz(struct client_state *csp); +#endif + /* * Revision control strings from this header and associated .c file */ diff --git a/jbsockets.c b/jbsockets.c index 182382b3..187fbc4d 100644 --- a/jbsockets.c +++ b/jbsockets.c @@ -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 $ @@ -594,6 +594,14 @@ int write_socket(jb_socket fd, const char *buf, size_t len) 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) diff --git a/jcc.c b/jcc.c index 408c649d..e4ecd01f 100644 --- 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 $ @@ -178,6 +178,11 @@ static int32 server_thread(void *data); #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? @@ -1279,7 +1284,12 @@ static char *get_request_line(struct client_state *csp) 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)) { @@ -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 /********************************************************************* * @@ -2539,6 +2623,7 @@ static void handle_established_connection(struct client_state *csp, csp->server_connection.timestamp = time(NULL); } + /********************************************************************* * * 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 /********************************************************************* * @@ -3158,7 +3303,7 @@ static int32 server_thread(void *data) * 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] " @@ -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) */ - "[--version] [configfile]\n" - "Aborting\n", myname); + "[--version] [configfile]\n", + name); + +#ifdef FUZZ + show_fuzz_usage(name); +#endif + + printf("Aborting\n"); exit(2); @@ -3326,7 +3477,6 @@ static void initialize_mutexes(void) #endif /* def MUTEX_LOCKS_AVAILABLE */ } - /********************************************************************* * * 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 +#ifdef FUZZ + char *fuzz_input_type = NULL; + char *fuzz_input_file = NULL; +#endif Argc = argc; Argv = argv; @@ -3492,7 +3646,20 @@ int main(int argc, char **argv) { 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) { /* @@ -3596,6 +3763,13 @@ int main(int argc, char **argv) # 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()); diff --git a/jcc.h b/jcc.h index 43b765fb..083348af 100644 --- a/jcc.h +++ b/jcc.h @@ -1,6 +1,6 @@ #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 $ @@ -117,6 +117,12 @@ int main(int argc, char **argv); 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 */ /* diff --git a/loaders.c b/loaders.c index 1fdeabfc..135b2084 100644 --- 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 $ @@ -74,7 +74,9 @@ const char loaders_h_rcs[] = LOADERS_H_VERSION; 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); +#endif 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); + 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) { + /* XXX: Why do we allow this anyway? */ *p = '\0'; *dest = buf; return JB_ERR_OK; diff --git a/loaders.h b/loaders.h index 5e654029..54c4aa27 100644 --- a/loaders.h +++ b/loaders.h @@ -1,6 +1,6 @@ #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 $ @@ -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); +#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); diff --git a/parsers.c b/parsers.c index b8f636d3..57e5cdeb 100644 --- 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 $ @@ -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 */ +#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); +#endif 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. */ - 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)) { diff --git a/pcrs.c b/pcrs.c index 955ccf8b..0553a53e 100644 --- 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 $ @@ -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 @@ -212,7 +244,14 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr 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; /* @@ -233,6 +272,10 @@ static pcrs_substitute *pcrs_compile_replacement(const char *replacement, int tr } memset(r, '\0', sizeof(pcrs_substitute)); +#ifdef FUZZ + r_stack = r; +#endif + length = strlen(replacement); if (NULL == (text = (char *)malloc(length + 1))) diff --git a/pcrs.h b/pcrs.h index 9798db1c..dc9533d1 100644 --- 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 @@ -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 */ +#ifdef FUZZ +extern pcrs_substitute *pcrs_compile_fuzzed_replacement(const char *replacement, int *errptr); +#endif + #endif /* ndef PCRS_H_INCLUDED */ /* diff --git a/project.h b/project.h index dcd2c313..e7c7323d 100644 --- a/project.h +++ b/project.h @@ -1,7 +1,7 @@ #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 $ @@ -861,6 +861,12 @@ struct reusable_connection */ #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 diff --git a/urlmatch.c b/urlmatch.c index 4839c2b1..37802c5f 100644 --- a/urlmatch.c +++ b/urlmatch.c @@ -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 $ @@ -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; - char rebuf[BUFFER_SIZE]; const char *fmt = NULL; + char *rebuf; + size_t rebuf_size; assert(pattern); - assert(strlen(pattern) < sizeof(rebuf) - 2); 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); } - + rebuf_size = strlen(pattern) + strlen(fmt); + rebuf = malloc_or_die(rebuf_size); *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) { - 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); + freez(rebuf); return JB_ERR_PARSE; } + freez(rebuf); return JB_ERR_OK;