

Heap overflow fix reported and resolved by Robert Matthews <rob tigertech.com>.



--- ../mod_fcgid.2.2-o/fcgid_bucket.c	2013-11-05 13:56:20.000000000 +0100
+++ ./fcgid_bucket.c	2013-11-05 14:00:41.000000000 +0100
@@ -1,274 +1,277 @@
 #include "fcgid_bucket.h"
 #include "fcgid_protocol.h"
 #include "fcgid_bridge.h"
 
 #define FCGID_FEED_LEN 8192
 static apr_status_t fcgid_feed_data(fcgid_bucket_ctx * ctx,
 									apr_bucket_alloc_t * bucketalloc,
 									char **buffer, apr_size_t * bufferlen)
 {
 	server_rec *main_server = ctx->ipc.request->server;
 	apr_status_t rv;
 
 	if (!ctx->buffer) {
 		*buffer = apr_bucket_alloc(FCGID_FEED_LEN, bucketalloc);
 		if (!buffer)
 			return APR_ENOMEM;
 
 		*bufferlen = FCGID_FEED_LEN;
 		if ((rv =
 			 proc_read_ipc(main_server, &ctx->ipc, *buffer,
 						   bufferlen)) != APR_SUCCESS) {
 			ctx->has_error = 1;
 			apr_bucket_free(*buffer);
 			return rv;
 		}
 
 		ctx->buffer =
 			apr_bucket_heap_create(*buffer, FCGID_FEED_LEN,
 								   apr_bucket_free, bucketalloc);
 		if (*bufferlen != FCGID_FEED_LEN) {
 			apr_bucket *buckettmp;
 
 			apr_bucket_split(ctx->buffer, *bufferlen);
 			buckettmp = APR_BUCKET_NEXT(ctx->buffer);
 			apr_bucket_delete(buckettmp);
 		}
 	} else {
 		apr_bucket_read(ctx->buffer, (const char **) buffer, bufferlen,
 						APR_BLOCK_READ);
 	}
 	return APR_SUCCESS;
 }
 
 static void fcgid_ignore_bytes(fcgid_bucket_ctx * ctx,
 							   apr_size_t ignorebyte)
 {
 	apr_bucket *buckettmp;
 
 	if (ignorebyte == ctx->buffer->length) {
 		apr_bucket_destroy(ctx->buffer);
 		ctx->buffer = NULL;
 	} else {
 		apr_bucket_split(ctx->buffer, ignorebyte);
 		buckettmp = ctx->buffer;
 		ctx->buffer = APR_BUCKET_NEXT(ctx->buffer);
 		apr_bucket_delete(buckettmp);
 	}
 }
 
 static apr_status_t fcgid_header_bucket_read(apr_bucket * b,
 											 const char **str,
 											 apr_size_t * len,
 											 apr_read_type_e block)
 {
 	fcgid_bucket_ctx *ctx = (fcgid_bucket_ctx *) b->data;
 	server_rec *main_server = ctx->ipc.request->server;
 	apr_status_t rv;
 	apr_size_t hasread, bodysize;
 	FCGI_Header header;
 	apr_bucket *curbucket = b;
 
 	/* Keep reading until I get a fastcgi header */
 	hasread = 0;
 	while (hasread < sizeof(header)) {
 		char *buffer;
 		apr_size_t bufferlen, putsize;
 
 		/* Feed some data if necessary */
 		if ((rv =
 			 fcgid_feed_data(ctx, b->list, &buffer,
 							 &bufferlen)) != APR_SUCCESS)
 			return rv;
 
 		/* Initialize header */
 		putsize = fcgid_min(bufferlen, sizeof(header) - hasread);
 		memcpy((char*)(&header) + hasread, buffer, putsize);
 		hasread += putsize;
 
 		/* Ignore the bytes that have read */
 		fcgid_ignore_bytes(ctx, putsize);
 	}
 
 	/* Get the body size */
 	bodysize = header.contentLengthB1;
 	bodysize <<= 8;
 	bodysize += header.contentLengthB0;
 
 	/* Handle FCGI_STDERR body, write the content to log file */
 	if (header.type == FCGI_STDERR) {
 		char *logbuf = apr_bucket_alloc(APR_BUCKET_BUFF_SIZE, b->list);
 		char *line;
+		apr_size_t hasput;
 
 		if (!logbuf)
 			return APR_ENOMEM;
 		memset(logbuf, 0, APR_BUCKET_BUFF_SIZE);
 
 		hasread = 0;
+		hasput = 0;
 		while (hasread < bodysize) {
 			char *buffer;
 			apr_size_t bufferlen, canput, willput;
 
 			/* Feed some data if necessary */
 			if ((rv =
 				 fcgid_feed_data(ctx, b->list, &buffer,
 								 &bufferlen)) != APR_SUCCESS) {
 				apr_bucket_free(logbuf);
 				return rv;
 			}
 
 			canput = fcgid_min(bufferlen, bodysize - hasread);
 			willput =
-				fcgid_min(canput, APR_BUCKET_BUFF_SIZE - hasread - 1);
-			memcpy(logbuf + hasread, buffer, willput);
+				fcgid_min(canput, APR_BUCKET_BUFF_SIZE - hasput - 1);
+			memcpy(logbuf + hasput, buffer, willput);
 			hasread += canput;
+			hasput += willput;
 
 			/* Ignore the "canput" bytes */
 			fcgid_ignore_bytes(ctx, canput);
 		}
 
 		/* Now I get the log data, write log and release the buffer */
 		line = logbuf;
 		while (*line) {
 			char *end = strpbrk(line, "\r\n");
 
 			if (end != NULL) {
 				*end = '\0';
 			}
 			ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server,
 						 "mod_fcgid: stderr: %s", line);
 			if (end == NULL) {
 				break;
 			}
 			++end;
 			line = end + strspn(end, "\r\n");
 		}
 
 		apr_bucket_free(logbuf);
 	}
 
 	/* if( header.type==FCGI_STDERR ) */
 	/* Now handle FCGI_STDOUT */
 	else if (header.type == FCGI_STDOUT) {
 		hasread = 0;
 		while (hasread < bodysize) {
 			char *buffer;
 			apr_size_t bufferlen, canput;
 			apr_bucket *buckettmp;
 
 			/* Feed some data if necessary */
 			if ((rv =
 				 fcgid_feed_data(ctx, b->list, &buffer,
 								 &bufferlen)) != APR_SUCCESS)
 				return rv;
 
 			canput = fcgid_min(bufferlen, bodysize - hasread);
 
 			/* Change the current bucket to refer to what we read */
 			buckettmp = ctx->buffer;
 			if (canput == (bodysize - hasread)) {
 				apr_bucket_split(ctx->buffer, canput);
 				ctx->buffer = APR_BUCKET_NEXT(ctx->buffer);
 				APR_BUCKET_REMOVE(buckettmp);
 			} else {
 				/* canput==bufferlen */
 				ctx->buffer = NULL;
 			}
 
 			APR_BUCKET_INSERT_AFTER(curbucket, buckettmp);
 			curbucket = buckettmp;
 			hasread += canput;
 		}						/* while( hasread<bodysize ) */
 	}
 
 	/* if( header.type==FCGI_STDOUT ) */
 	/* Now FCGI_END_REQUEST */
 	else if (header.type == FCGI_END_REQUEST) {
 		/* Just ignore the body */
 		hasread = 0;
 		while (hasread < bodysize) {
 			char *buffer;
 			apr_size_t bufferlen, canignore;
 
 			/* Feed some data if necessary */
 			if ((rv =
 				 fcgid_feed_data(ctx, b->list, &buffer,
 								 &bufferlen)) != APR_SUCCESS)
 				return rv;
 
 			canignore = fcgid_min(bufferlen, bodysize);
 			hasread += canignore;
 
 			/* Ignore the bytes */
 			fcgid_ignore_bytes(ctx, canignore);
 		}
 	}
 
 	/* Now ignore padding data */
 	hasread = 0;
 	while (hasread < header.paddingLength) {
 		char *buffer;
 		apr_size_t bufferlen, canignore;
 
 		/* Feed some data if necessary */
 		if ((rv =
 			 fcgid_feed_data(ctx, b->list, &buffer,
 							 &bufferlen)) != APR_SUCCESS)
 			return rv;
 
 		canignore = fcgid_min(bufferlen, header.paddingLength - hasread);
 		hasread += canignore;
 
 		/* Ignore the bytes */
 		fcgid_ignore_bytes(ctx, canignore);
 	}
 
 	/* Tail another fastcgi header bucket if it's not ending */
 	if (header.type != FCGI_END_REQUEST) {
 		apr_bucket *headerbucket =
 			ap_bucket_fcgid_header_create(b->list, ctx);
 		if (!headerbucket)
 			return APR_ENOMEM;
 		APR_BUCKET_INSERT_AFTER(curbucket, headerbucket);
 	} else {
 		/* Release the process ASAP */
 		if ((rv = apr_pool_cleanup_run(ctx->ipc.request->pool,
 									   ctx,
 									   bucket_ctx_cleanup)) != APR_SUCCESS)
 			return rv;
 	}
 
 	b = apr_bucket_immortal_make(b, "", 0);
 	return apr_bucket_read(b, str, len, APR_BLOCK_READ);
 }
 
 apr_bucket *ap_bucket_fcgid_header_make(apr_bucket * b,
 										fcgid_bucket_ctx * ctx)
 {
 	b->length = (apr_size_t) (-1);
 	b->start = -1;
 	b->data = ctx;
 	b->type = &ap_bucket_type_fcgid_header;
 
 	return b;
 }
 
 apr_bucket *ap_bucket_fcgid_header_create(apr_bucket_alloc_t * list,
 										  fcgid_bucket_ctx * ctx)
 {
 	apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
 
 	APR_BUCKET_INIT(b);
 	b->free = apr_bucket_free;
 	b->list = list;
 	return ap_bucket_fcgid_header_make(b, ctx);
 }
 
 const apr_bucket_type_t ap_bucket_type_fcgid_header = {
 	"FCGID_HEADER", 5, APR_BUCKET_DATA,
 	apr_bucket_destroy_noop,
 	fcgid_header_bucket_read,
 	apr_bucket_setaside_notimpl,
 	apr_bucket_split_notimpl,
 	apr_bucket_copy_notimpl
 };
