CVE-2017-9203: Out-of-bounds Write (CWE-787)
value: imagew-main.c:960:12 in libimageworsener.a in ImageWorsener 1.3.1 allows remote attackers to cause a denial of service (buffer underflow) via a crafted image, related to imagew-bmp.c.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 6.5
Published: 2017-05-23T04:29Z
Repository: https://github.com/jsummers/imageworsener
Language: C
Files changed: src/imagew-bmp.c, src/imagew-main.c
Commit date: 2017-05-11 12:15:55 -0400
Commit message: Fixed a bug that could cause invalid memory to be accessed

The bug could happen when transparency is removed from an image.
Also fixed a semi-related BMP error handling logic bug.
Fixes issue #21

Diff: diff --git a/src/imagew-bmp.c b/src/imagew-bmp.c
index 9e51106..811da1d 100644
--- a/src/imagew-bmp.c
+++ b/src/imagew-bmp.c
@@ -845,15 +845,17 @@ static int bmpr_read_rle(struct iwbmprcontext *rctx)
 
 	if(!(rctx->compression==IWBMP_BI_RLE8 && rctx->bitcount==8) &&
 		!(rctx->compression==IWBMP_BI_RLE4 && rctx->bitcount==4))
 	{
 		iw_set_error(rctx->ctx,"Compression type incompatible with image type");
+		goto done;
 	}
 
 	if(rctx->topdown) {
 		// The documentation says that top-down images may not be compressed.
 		iw_set_error(rctx->ctx,"Compression not allowed with top-down images");
+		goto done;
 	}
 
 	// RLE-compressed BMP images don't have to assign a color to every pixel,
 	// and it's reasonable to interpret undefined pixels as transparent.
 	// I'm not going to worry about handling compressed BMP images as
diff --git a/src/imagew-main.c b/src/imagew-main.c
index 2fcf9e5..5a59c05 100644
--- a/src/imagew-main.c
+++ b/src/imagew-main.c
@@ -920,12 +920,10 @@ done:
 	if(inpix_tofree) iw_free(ctx,inpix_tofree);
 	if(outpix_tofree) iw_free(ctx,outpix_tofree);
 	return retval;
 }
 
-// 'handle_alpha_flag' must be set if an alpha channel exists and this is not
-// the alpha channel.
 static int iw_process_rows_intermediate_to_final(struct iw_context *ctx, int intermed_channel,
 	const struct iw_csdescr *out_csdescr)
 {
 	int i,j;
 	int z;
@@ -949,17 +947,31 @@ static int iw_process_rows_intermediate_to_final(struct iw_context *ctx, int int
 
 	iw_tmpsample *in_pix = NULL;
 	iw_tmpsample *out_pix = NULL;
 	int num_in_pix;
 	int num_out_pix;
+	struct iw_channelinfo_out default_ci_out;
 
 	num_in_pix = ctx->intermed_canvas_width;
 	num_out_pix = ctx->img2.width;
 
 	int_ci = &ctx->intermed_ci[intermed_channel];
 	output_channel = int_ci->corresponding_output_channel;
-	out_ci = &ctx->img2_ci[output_channel];
+	if(output_channel>=0) {
+		out_ci = &ctx->img2_ci[output_channel];
+	}
+	else {
+		// If there is no output channelinfo struct, create a temporary one to
+		// use.
+		// TODO: This is admittedly ugly, but we use these settings for a few
+		// things even when there is no corresponding output channel, and I
+		// don't remember exactly why.
+		iw_zeromem(&default_ci_out, sizeof(struct iw_channelinfo_out));
+		default_ci_out.channeltype = IW_CHANNELTYPE_NONALPHA;
+		out_ci = &default_ci_out;
+	}
+
 	is_alpha_channel = (int_ci->channeltype==IW_CHANNELTYPE_ALPHA);
 	bkgd_has_transparency = iw_bkgd_has_transparency(ctx);
 
 	inpix_tofree = (iw_tmpsample*)iw_malloc(ctx, num_in_pix * sizeof(iw_tmpsample));
 	in_pix = inpix_tofree;

Vulnerable code: // 'handle_alpha_flag' must be set if an alpha channel exists and this is not
// the alpha channel.
	out_ci = &ctx->img2_ci[output_channel];
Fixed code: 		goto done;
		goto done;
	struct iw_channelinfo_out default_ci_out;
	if(output_channel>=0) {
		out_ci = &ctx->img2_ci[output_channel];
	}
	else {
		// If there is no output channelinfo struct, create a temporary one to
		// use.
		// TODO: This is admittedly ugly, but we use these settings for a few
		// things even when there is no corresponding output channel, and I
		// don't remember exactly why.
		iw_zeromem(&default_ci_out, sizeof(struct iw_channelinfo_out));
		default_ci_out.channeltype = IW_CHANNELTYPE_NONALPHA;
		out_ci = &default_ci_out;
	}