Commit message: Double-check that the input image's density is valid

Fixes a bug that could result in division by zero, at least for a JPEG
source image.
Fixes issues #19, #20
CVE: CVE-2017-9201
CWE: CWE-369
Vulnerability description: imagew-cmd.c:850:46 in libimageworsener.a in ImageWorsener 1.3.1 allows remote attackers to cause a denial of service (divide-by-zero error) via a crafted image, related to imagew-api.c.

Project: imageworsener

static void default_resize_settings(struct iw_resize_settings *rs)
{
	int i;
	rs->family = IW_RESIZETYPE_AUTO;
	rs->edge_policy = IW_EDGE_POLICY_STANDARD;
	rs->blur_factor = 1.0;
	rs->translate = 0.0;
	for(i=0;i<3;i++) {
		rs->channel_offset[i] = 0.0;
	}
}

Project: imageworsener

IW_IMPL(int) iw_check_image_dimensions(struct iw_context *ctx, int w, int h)
{
	if(w>ctx->max_width || h>ctx->max_height) {
		iw_set_errorf(ctx,"Image dimensions too large (%d\xc3\x97%d)",w,h);
		return 0;
	}

	if(w<1 || h<1) {
		iw_set_errorf(ctx,"Invalid image dimensions (%d\xc3\x97%d)",w,h);
		return 0;
	}

	return 1;
}

Project: imageworsener

IW_IMPL(unsigned int) iw_color_get_int_sample(struct iw_color *clr, int channel,
	unsigned int maxcolorcode)
{
	int n;
	n = (int)(0.5+(clr->c[channel] * (double)maxcolorcode));
	if(n<0) n=0;
	else if(n>(int)maxcolorcode) n=(int)maxcolorcode;
	return (unsigned int)n;
}

Project: imageworsener

IW_IMPL(void) iw_destroy_context(struct iw_context *ctx)
{
	int i;
	if(!ctx) return;
	if(ctx->req.options) {
		for(i=0; i<=ctx->req.options_count; i++) {
			iw_free(ctx, ctx->req.options[i].name);
			iw_free(ctx, ctx->req.options[i].val);
		}
		iw_free(ctx, ctx->req.options);
	}
	if(ctx->img1.pixels) iw_free(ctx,ctx->img1.pixels);
	if(ctx->img2.pixels) iw_free(ctx,ctx->img2.pixels);
	if(ctx->error_msg) iw_free(ctx,ctx->error_msg);
	if(ctx->optctx.tmp_pixels) iw_free(ctx,ctx->optctx.tmp_pixels);
	if(ctx->optctx.palette) iw_free(ctx,ctx->optctx.palette);
	if(ctx->input_color_corr_table) iw_free(ctx,ctx->input_color_corr_table);
	if(ctx->output_rev_color_corr_table) iw_free(ctx,ctx->output_rev_color_corr_table);
	if(ctx->nearest_color_table) iw_free(ctx,ctx->nearest_color_table);
	if(ctx->prng) iwpvt_prng_destroy(ctx,ctx->prng);
	iw_free(ctx,ctx);
}

Project: imageworsener

IW_IMPL(char*) iw_get_copyright_string(struct iw_context *ctx, char *dst, int dstlen)
{
	iw_translatef(ctx,0,dst,dstlen,"Copyright \xc2\xa9 %s %s",IW_COPYRIGHT_YEAR,"Jason Summers");
	return dst;
}

Project: imageworsener

IW_IMPL(const char*) iw_get_errormsg(struct iw_context *ctx, char *buf, int buflen)
{
	if(ctx->error_msg) {
		iw_strlcpy(buf,ctx->error_msg,buflen);
	}
	else {
		iw_translate(ctx,IW_TRANSLATEFLAG_ERRORMSG,buf,buflen,"Error message not available");
	}

	return buf;
}

Project: imageworsener

IW_IMPL(const char*) iw_get_option(struct iw_context *ctx, const char *name)
{
	int i;
	for(i=0; i<ctx->req.options_count; i++) {
		if(ctx->req.options[i].name && !strcmp(ctx->req.options[i].name, name)) {
			return ctx->req.options[i].val;
		}
	}
	return NULL;
}

Project: imageworsener

IW_IMPL(void) iw_get_output_colorspace(struct iw_context *ctx, struct iw_csdescr *csdescr)
{
	*csdescr = ctx->img2cs; // struct copy
}

Project: imageworsener

IW_IMPL(void) iw_get_output_image(struct iw_context *ctx, struct iw_image *img)
{
	int k;

	iw_zeromem(img,sizeof(struct iw_image));
	img->width = ctx->optctx.width;
	img->height = ctx->optctx.height;
	img->imgtype = ctx->optctx.imgtype;
	img->sampletype = ctx->img2.sampletype;
	img->bit_depth = ctx->optctx.bit_depth;
	img->pixels = (iw_byte*)ctx->optctx.pixelsptr;
	img->bpr = ctx->optctx.bpr;
	img->density_code = ctx->img2.density_code;
	img->density_x = ctx->img2.density_x;
	img->density_y = ctx->img2.density_y;
	img->rendering_intent = ctx->img2.rendering_intent;

	img->has_bkgdlabel = ctx->optctx.has_bkgdlabel;
	for(k=0;k<4;k++) {
		if(ctx->optctx.bit_depth==8) {
			img->bkgdlabel.c[k] = ((double)ctx->optctx.bkgdlabel[k])/255.0;
		}
		else {
			img->bkgdlabel.c[k] = ((double)ctx->optctx.bkgdlabel[k])/65535.0;
		}
	}

	img->has_colorkey_trns = ctx->optctx.has_colorkey_trns;
	img->colorkey[0] = ctx->optctx.colorkey[0];
	img->colorkey[1] = ctx->optctx.colorkey[1];
	img->colorkey[2] = ctx->optctx.colorkey[2];
	if(ctx->reduced_output_maxcolor_flag) {
		img->reduced_maxcolors = 1;
		if(IW_IMGTYPE_IS_GRAY(img->imgtype)) {
			img->maxcolorcode[IW_CHANNELTYPE_GRAY] = ctx->img2_ci[0].maxcolorcode_int;
			if(IW_IMGTYPE_HAS_ALPHA(img->imgtype)) {
				img->maxcolorcode[IW_CHANNELTYPE_ALPHA] = ctx->img2_ci[1].maxcolorcode_int;
			}
		}
		else {
			img->maxcolorcode[IW_CHANNELTYPE_RED]   = ctx->img2_ci[0].maxcolorcode_int;
			img->maxcolorcode[IW_CHANNELTYPE_GREEN] = ctx->img2_ci[1].maxcolorcode_int;
			img->maxcolorcode[IW_CHANNELTYPE_BLUE]  = ctx->img2_ci[2].maxcolorcode_int;
			if(IW_IMGTYPE_HAS_ALPHA(img->imgtype)) {
				img->maxcolorcode[IW_CHANNELTYPE_ALPHA] = ctx->img2_ci[3].maxcolorcode_int;
			}
		}
	}
}

Project: imageworsener

IW_IMPL(int) iw_get_sample_size(void)
{
	return (int)sizeof(iw_float32);
}

Project: imageworsener

IW_IMPL(void*) iw_get_userdata(struct iw_context *ctx)
{
	return ctx->userdata;
}

Project: imageworsener

IW_IMPL(double) iw_get_value_dbl(struct iw_context *ctx, int code)
{
	double ret = 0.0;

	switch(code) {
	case IW_VAL_TRANSLATE_X:
		ret = ctx->resize_settings[IW_DIMENSION_H].translate;
		break;
	case IW_VAL_TRANSLATE_Y:
		ret = ctx->resize_settings[IW_DIMENSION_V].translate;
		break;
	}

	return ret;
}

Project: imageworsener

IW_IMPL(int) iw_get_version_int(void)
{
	return IW_VERSION_INT;
}

Project: imageworsener

IW_IMPL(char*) iw_get_version_string(struct iw_context *ctx, char *s, int s_len)
{
	int ver;
	ver = iw_get_version_int();
	iw_snprintf(s,s_len,"%d.%d.%d",
		(ver&0xff0000)>>16, (ver&0xff00)>>8, (ver&0xff) );
	return s;
}

Project: imageworsener

IW_IMPL(struct iw_zlib_module*) iw_get_zlib_module(struct iw_context *ctx)
{
	return ctx->zlib_module;
}

Project: imageworsener

IW_IMPL(int) iw_imgtype_num_channels(int t)
{
	switch(t) {
	case IW_IMGTYPE_RGBA:
		return 4;
	case IW_IMGTYPE_RGB:
		return 3;
	case IW_IMGTYPE_GRAYA:
		return 2;
	}
	return 1;
}

Project: imageworsener

IW_IMPL(int) iw_is_valid_density(double density_x, double density_y, int density_code)
{
	if(density_x<0.0001 || density_y<0.0001) return 0;
	if(density_x>10000000.0 || density_y>10000000.0) return 0;
	if(density_x/10.0>density_y) return 0;
	if(density_y/10.0>density_x) return 0;
	if(density_code!=IW_DENSITY_UNITS_UNKNOWN && density_code!=IW_DENSITY_UNITS_PER_METER)
		return 0;
	return 1;
}

Project: imageworsener

IW_IMPL(void) iw_make_gamma_csdescr(struct iw_csdescr *cs, double gamma)
{
	cs->cstype = IW_CSTYPE_GAMMA;
	cs->gamma = gamma;
	if(cs->gamma<0.1) cs->gamma=0.1;
	if(cs->gamma>10.0) cs->gamma=10.0;
	cs->srgb_intent = 0;
	optimize_csdescr(cs);
}

Project: imageworsener

IW_IMPL(void) iw_make_linear_csdescr(struct iw_csdescr *cs)
{
	cs->cstype = IW_CSTYPE_LINEAR;
	cs->gamma = 0.0;
	cs->srgb_intent = 0;
}

Project: imageworsener

IW_IMPL(void) iw_make_rec709_csdescr(struct iw_csdescr *cs)
{
	cs->cstype = IW_CSTYPE_REC709;
	cs->gamma = 0.0;
}

Project: imageworsener

IW_IMPL(void) iw_make_srgb_csdescr_2(struct iw_csdescr *cs)
{
	cs->cstype = IW_CSTYPE_SRGB;
	cs->gamma = 0.0;
}

Project: imageworsener

IW_IMPL(void) iw_reorient_image(struct iw_context *ctx, unsigned int x)
{
	static const unsigned int transpose_tbl[8] = { 4,6,5,7,0,2,1,3 };
	int tmpi;
	double tmpd;

	x = x & 0x07;

	if(x&0x04) {
		ctx->img1.orient_transform = transpose_tbl[ctx->img1.orient_transform];

		tmpi = ctx->img1.width;
		ctx->img1.width = ctx->img1.height;
		ctx->img1.height = tmpi;

		tmpd = ctx->img1.density_x;
		ctx->img1.density_x = ctx->img1.density_y;
		ctx->img1.density_y = tmpd;
	}

	ctx->img1.orient_transform ^= (x&0x03);
}

Project: imageworsener

IW_IMPL(void) iw_set_allow_opt(struct iw_context *ctx, int opt, int n)
{
	iw_byte v;
	v = n?1:0;

	switch(opt) {
	case IW_OPT_GRAYSCALE: ctx->opt_grayscale = v; break;
	case IW_OPT_PALETTE: ctx->opt_palette = v; break;
	case IW_OPT_16_TO_8: ctx->opt_16_to_8 = v; break;
	case IW_OPT_STRIP_ALPHA: ctx->opt_strip_alpha = v; break;
	case IW_OPT_BINARY_TRNS: ctx->opt_binary_trns = v; break;
	}
}

Project: imageworsener

IW_IMPL(void) iw_set_apply_bkgd(struct iw_context *ctx, double r, double g, double b)
{
	struct iw_color clr;
	clr.c[IW_CHANNELTYPE_RED]=r;
	clr.c[IW_CHANNELTYPE_GREEN]=g;
	clr.c[IW_CHANNELTYPE_BLUE]=b;
	clr.c[IW_CHANNELTYPE_ALPHA]=1.0;
	iw_set_apply_bkgd_2(ctx, &clr);
}

Project: imageworsener

IW_IMPL(void) iw_set_bkgd_checkerboard_2(struct iw_context *ctx, int checkersize,
	const struct iw_color *clr)
{
	ctx->req.bkgd_checkerboard=1;
	ctx->bkgd_check_size=checkersize;
	ctx->req.bkgd2 = *clr;
}

Project: imageworsener

IW_IMPL(void) iw_set_bkgd_checkerboard_origin(struct iw_context *ctx, int x, int y)
{
	ctx->bkgd_check_origin[IW_DIMENSION_H] = x;
	ctx->bkgd_check_origin[IW_DIMENSION_V] = y;
}

Project: imageworsener

IW_IMPL(void) iw_set_channel_offset(struct iw_context *ctx, int channeltype, int dimension, double offs)
{
	if(channeltype<0 || channeltype>2) return;
	if(dimension<0 || dimension>1) dimension=0;
	ctx->resize_settings[dimension].channel_offset[channeltype] = offs;
}

Project: imageworsener

IW_IMPL(void) iw_set_color_count(struct iw_context *ctx, int channeltype, int c)
{
	if(channeltype>=0 && channeltype<IW_NUM_CHANNELTYPES) {
		ctx->req.color_count[channeltype] = c;
	}

	switch(channeltype) {
	case IW_CHANNELTYPE_ALL:
		ctx->req.color_count[IW_CHANNELTYPE_ALPHA] = c;
	case IW_CHANNELTYPE_NONALPHA:
		ctx->req.color_count[IW_CHANNELTYPE_RED] = c;
		ctx->req.color_count[IW_CHANNELTYPE_GREEN] = c;
		ctx->req.color_count[IW_CHANNELTYPE_BLUE] = c;
		ctx->req.color_count[IW_CHANNELTYPE_GRAY] = c;
		break;
	}
}

Project: imageworsener

IW_IMPL(void) iw_set_error(struct iw_context *ctx, const char *s)
{
	char buf[IW_MSG_MAX];

	if(ctx->error_flag) return; // Only record the first error.
	iw_translate(ctx,IW_TRANSLATEFLAG_ERRORMSG,buf,sizeof(buf),s);
	iw_set_error_internal(ctx,buf);
}

Project: imageworsener

static void iw_set_error_internal(struct iw_context *ctx, const char *s)
{
	if(ctx->error_flag) return; // Only record the first error.
	ctx->error_flag = 1;

	if(!ctx->error_msg) {
		ctx->error_msg=iw_malloc_ex(ctx,IW_MALLOCFLAG_NOERRORS,IW_MSG_MAX*sizeof(char));
		if(!ctx->error_msg) {
			return;
		}
	}

	iw_strlcpy(ctx->error_msg,s,IW_MSG_MAX);
}

Project: imageworsener

IW_IMPL(void) iw_set_errorv(struct iw_context *ctx, const char *fmt, va_list ap)
{
	char buf[IW_MSG_MAX];

	if(ctx->error_flag) return; // Only record the first error.
	iw_translatev(ctx,IW_TRANSLATEFLAG_ERRORMSG,buf,sizeof(buf),fmt,ap);
	iw_set_error_internal(ctx,buf);
}

Project: imageworsener

IW_IMPL(void) iw_set_grayscale_weights(struct iw_context *ctx,
	double r, double g, double b)
{
	double tot;


	tot = r+g+b;
	if(tot==0.0) tot=1.0;
	ctx->grayscale_weight[0] = r/tot;
	ctx->grayscale_weight[1] = g/tot;
	ctx->grayscale_weight[2] = b/tot;
}

Project: imageworsener

IW_IMPL(void) iw_set_input_bkgd_label(struct iw_context *ctx, double r, double g, double b)
{
	struct iw_color clr;
	clr.c[0] = r;
	clr.c[1] = g;
	clr.c[2] = b;
	clr.c[3] = 1.0;
	iw_set_input_bkgd_label_2(ctx, &clr);
}

Project: imageworsener

IW_IMPL(void) iw_set_input_bkgd_label_2(struct iw_context *ctx, const struct iw_color *clr)
{
	ctx->img1_bkgd_label_set = 1;
	ctx->img1_bkgd_label_inputcs = *clr;
}

Project: imageworsener

IW_IMPL(void) iw_set_input_colorspace(struct iw_context *ctx, const struct iw_csdescr *csdescr)
{
	ctx->img1cs = *csdescr; // struct copy
	optimize_csdescr(&ctx->img1cs);
}

Project: imageworsener

IW_IMPL(void) iw_set_input_crop(struct iw_context *ctx, int x, int y, int w, int h)
{
	ctx->input_start_x = x;
	ctx->input_start_y = y;
	ctx->input_w = w;
	ctx->input_h = h;
}

Project: imageworsener

IW_IMPL(void) iw_set_input_image(struct iw_context *ctx, const struct iw_image *img)
{
	ctx->img1 = *img; // struct copy
}

Project: imageworsener

IW_IMPL(void) iw_set_input_max_color_code(struct iw_context *ctx, int input_channel, int c)
{
	if(input_channel>=0 && input_channel<IW_CI_COUNT) {
		ctx->img1_ci[input_channel].maxcolorcode_int = c;
	}
}

Project: imageworsener

IW_IMPL(void) iw_set_max_malloc(struct iw_context *ctx, size_t n)
{
	ctx->max_malloc = n;
}

Project: imageworsener

IW_IMPL(void) iw_set_option(struct iw_context *ctx, const char *name, const char *val)
{
#define IW_MAX_OPTIONS 32
	int i;

	if(val==NULL || val[0]=='\0') {
		val = "1";
	}

	if(!ctx->req.options) {
		ctx->req.options = iw_mallocz(ctx, IW_MAX_OPTIONS*sizeof(struct iw_option_struct));
		if(!ctx->req.options) return;
		ctx->req.options_numalloc = IW_MAX_OPTIONS;
		ctx->req.options_count = 0;
	}

	for(i=0; i<ctx->req.options_count; i++) {
		if(ctx->req.options[i].name && !strcmp(ctx->req.options[i].name, name)) {
			iw_free(ctx, ctx->req.options[i].val);
			ctx->req.options[i].val = iw_strdup(ctx, val);
			return;
		}
	}

	if(ctx->req.options_count>=IW_MAX_OPTIONS) return;
	ctx->req.options[ctx->req.options_count].name = iw_strdup(ctx, name);
	ctx->req.options[ctx->req.options_count].val = iw_strdup(ctx, val);
	ctx->req.options_count++;
}

Project: imageworsener

IW_IMPL(void) iw_set_output_bkgd_label(struct iw_context *ctx, double r, double g, double b)
{
	struct iw_color clr;
	clr.c[0] = r;
	clr.c[1] = g;
	clr.c[2] = b;
	clr.c[3] = 1.0;
	iw_set_output_bkgd_label_2(ctx, &clr);
}

Project: imageworsener

IW_IMPL(void) iw_set_output_colorspace(struct iw_context *ctx, const struct iw_csdescr *csdescr)
{
	ctx->req.output_cs = *csdescr; // struct copy
	optimize_csdescr(&ctx->req.output_cs);
	ctx->req.output_cs_valid = 1;
}

Project: imageworsener

 IW_IMPL(void) iw_set_output_density(struct iw_context *ctx,
   double x, double y, int code)
{
	ctx->img2.density_code = code;
	ctx->img2.density_x = x;
	ctx->img2.density_y = y;
}

Project: imageworsener

IW_IMPL(void) iw_set_output_depth(struct iw_context *ctx, int bps)
{
	ctx->req.output_depth = bps;
}

Project: imageworsener

IW_IMPL(void) iw_set_output_image_size(struct iw_context *ctx, double w, double h)
{
	ctx->req.out_true_width = w;
	if(ctx->req.out_true_width<0.01) ctx->req.out_true_width=0.01;
	ctx->req.out_true_height = h;
	if(ctx->req.out_true_height<0.01) ctx->req.out_true_height=0.01;
	ctx->req.out_true_valid = 1;
}

Project: imageworsener

IW_IMPL(void) iw_set_output_profile(struct iw_context *ctx, unsigned int n)
{
	ctx->output_profile = n;
}

Project: imageworsener

IW_IMPL(void) iw_set_random_seed(struct iw_context *ctx, int randomize, int rand_seed)
{
	ctx->randomize = randomize;
	ctx->random_seed = rand_seed;
}

Project: imageworsener

IW_IMPL(void) iw_set_translate_fn(struct iw_context *ctx, iw_translatefn_type xlatefn)
{
	ctx->translate_fn = xlatefn;
}

Project: imageworsener

IW_IMPL(void) iw_set_userdata(struct iw_context *ctx, void *userdata)
{
	ctx->userdata = userdata;
}

Project: imageworsener

IW_IMPL(void) iw_set_warning_fn(struct iw_context *ctx, iw_warningfn_type warnfn)
{
	ctx->warning_fn = warnfn;
}

Project: imageworsener

IW_IMPL(void) iw_translatef(struct iw_context *ctx, unsigned int flags,
	char *dst, size_t dstlen, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	iw_translatev(ctx,flags,dst,dstlen,fmt,ap);
	va_end(ap);
}

Project: imageworsener

IW_IMPL(void) iw_translatev(struct iw_context *ctx, unsigned int flags,
	char *dst, size_t dstlen, const char *fmt, va_list ap)
{
	char buf1[IW_MSG_MAX];
	char buf2[IW_MSG_MAX];

	if(!ctx || !ctx->translate_fn) {
		iw_vsnprintf(dst,dstlen,fmt,ap);
		return;
	}

	iw_translate(ctx,IW_TRANSLATEFLAG_FORMAT|flags,buf1,sizeof(buf1),fmt);
	iw_vsnprintf(buf2,sizeof(buf2),buf1,ap);
	iw_translate(ctx,IW_TRANSLATEFLAG_POSTFORMAT|flags,dst,dstlen,buf2);
}

Project: imageworsener

IW_IMPL(void) iw_warning(struct iw_context *ctx, const char *s)
{
	char buf[IW_MSG_MAX];

	if(!ctx->warning_fn) return;
	iw_translate(ctx,IW_TRANSLATEFLAG_WARNINGMSG,buf,sizeof(buf),s);
	iw_warning_internal(ctx,buf);
}

Project: imageworsener

static void iw_warning_internal(struct iw_context *ctx, const char *s)
{
	if(!ctx->warning_fn) return;

	(*ctx->warning_fn)(ctx,s);
}

Project: imageworsener

IW_IMPL(void) iw_warningf(struct iw_context *ctx, const char *fmt, ...)
{
	va_list ap;

	if(!ctx->warning_fn) return;
	va_start(ap, fmt);
	iw_warningv(ctx,fmt,ap);
	va_end(ap);
}

Project: imageworsener

IW_IMPL(void) iw_warningv(struct iw_context *ctx, const char *fmt, va_list ap)
{
	char buf[IW_MSG_MAX];

	if(!ctx->warning_fn) return;
	iw_translatev(ctx,IW_TRANSLATEFLAG_WARNINGMSG,buf,sizeof(buf),fmt,ap);
	iw_warning_internal(ctx,buf);
}

Project: imageworsener

static void optimize_csdescr(struct iw_csdescr *cs)
{
	if(cs->cstype!=IW_CSTYPE_GAMMA) return;
	if(cs->gamma>=0.999995 && cs->gamma<=1.000005) {
		cs->cstype = IW_CSTYPE_LINEAR;
	}
}