jpeg: Throw error when number of color components is unsupported

Explicitly check "3" or "4" output color components.

gdk-pixbuf assumed that the value of output_components to be either
3 or 4, but not an invalid value (9) or an unsupported value (1).

The way the buffer size was deduced was using a naive "== 4" check,
with a 1, 3 or 9 color component picture getting the same buffer size,
a size just sufficient for 3 color components, causing invalid writes
later when libjpeg-turbo was decoding the image.

CVE-2017-2862

Sent by from Marcin 'Icewall' Noga of Cisco Talos

https://bugzilla.gnome.org/show_bug.cgi?id=784866

gdk_pixbuf__jpeg_image_load_increment (gpointer data,
                                       const guchar *buf, guint size,
                                       GError **error)
{
	JpegProgContext *context = (JpegProgContext *)data;
	struct           jpeg_decompress_struct *cinfo;
	my_src_ptr       src;
	guint            num_left, num_copy;
	guint            last_num_left, last_bytes_left;
	guint            spinguard;
	gboolean         first;
	const guchar    *bufhd;
	gint             width, height;
	char             otag_str[5];
	gchar 		*icc_profile_base64;
	char            *density_str;
	JpegExifContext  exif_context = { 0, };
	gboolean	 retval;

	g_return_val_if_fail (context != NULL, FALSE);
	g_return_val_if_fail (buf != NULL, FALSE);

	src = (my_src_ptr) context->cinfo.src;

	cinfo = &context->cinfo;

        context->jerr.error = error;

	/* check for fatal error */
	if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
		retval = FALSE;
		goto out;
	}

	/* skip over data if requested, handle unsigned int sizes cleanly */
	/* only can happen if we've already called jpeg_get_header once   */
	if (context->src_initialized && src->skip_next) {
		if (src->skip_next > size) {
			src->skip_next -= size;
			retval = TRUE;
			goto out;
		} else {
			num_left = size - src->skip_next;
			bufhd = buf + src->skip_next;
			src->skip_next = 0;
		}
	} else {
		num_left = size;
		bufhd = buf;
	}

	if (num_left == 0) {
		retval = TRUE;
		goto out;
	}

	last_num_left = num_left;
	last_bytes_left = 0;
	spinguard = 0;
	first = TRUE;
	while (TRUE) {

		/* handle any data from caller we haven't processed yet */
		if (num_left > 0) {
			if(src->pub.bytes_in_buffer &&
			   src->pub.next_input_byte != src->buffer)
				memmove(src->buffer, src->pub.next_input_byte,
					src->pub.bytes_in_buffer);

			num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer,
					num_left);

			memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy);
			src->pub.next_input_byte = src->buffer;
			src->pub.bytes_in_buffer += num_copy;
			bufhd += num_copy;
			num_left -= num_copy;
		}

                /* did anything change from last pass, if not return */
                if (first) {
                        last_bytes_left = src->pub.bytes_in_buffer;
                        first = FALSE;
                } else if (src->pub.bytes_in_buffer == last_bytes_left
			   && num_left == last_num_left) {
                        spinguard++;
		} else {
                        last_bytes_left = src->pub.bytes_in_buffer;
			last_num_left = num_left;
		}

		/* should not go through twice and not pull bytes out of buf */
		if (spinguard > 2) {
			retval = TRUE;
			goto out;
		}

		/* try to load jpeg header */
		if (!context->got_header) {
			int rc;
			gchar* comment;

			jpeg_save_markers (cinfo, JPEG_APP0+1, 0xffff);
			jpeg_save_markers (cinfo, JPEG_APP0+2, 0xffff);
			jpeg_save_markers (cinfo, JPEG_COM, 0xffff);
			rc = jpeg_read_header (cinfo, TRUE);
			context->src_initialized = TRUE;

			if (rc == JPEG_SUSPENDED)
				continue;

			context->got_header = TRUE;

			/* parse exif data */
			jpeg_parse_exif (&exif_context, cinfo);

			width = cinfo->image_width;
			height = cinfo->image_height;
			if (context->size_func) {
				(* context->size_func) (&width, &height, context->user_data);
				if (width == 0 || height == 0) {
					g_set_error_literal (error,
                                                             GDK_PIXBUF_ERROR,
                                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                                             _("Transformed JPEG has zero width or height."));
					retval = FALSE;
					goto out;
				}
			}

			cinfo->scale_num = 1;
			for (cinfo->scale_denom = 2; cinfo->scale_denom <= 8; cinfo->scale_denom *= 2) {
				jpeg_calc_output_dimensions (cinfo);
				if (cinfo->output_width < width || cinfo->output_height < height) {
					cinfo->scale_denom /= 2;
					break;
				}
			}
			jpeg_calc_output_dimensions (cinfo);

			context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
							  cinfo->output_components == 4 ? TRUE : FALSE,
							  8,
							  cinfo->output_width,
							  cinfo->output_height);

			if (context->pixbuf == NULL) {
                                g_set_error_literal (error,
                                                     GDK_PIXBUF_ERROR,
                                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                                     _("Couldn't allocate memory for loading JPEG file"));
                                retval = FALSE;
				goto out;
			}

			comment = jpeg_get_comment (cinfo);
			if (comment != NULL) {
				gdk_pixbuf_set_option (context->pixbuf, "comment", comment);
				g_free (comment);
			}

			switch (cinfo->density_unit) {
			case 1:
				/* Dots per inch (no conversion required) */
				density_str = g_strdup_printf ("%d", cinfo->X_density);
				gdk_pixbuf_set_option (context->pixbuf, "x-dpi", density_str);
				g_free (density_str);
				density_str = g_strdup_printf ("%d", cinfo->Y_density);
				gdk_pixbuf_set_option (context->pixbuf, "y-dpi", density_str);
				g_free (density_str);
				break;
			case 2:
				/* Dots per cm - convert into dpi */
				density_str = g_strdup_printf ("%d", DPCM_TO_DPI (cinfo->X_density));
				gdk_pixbuf_set_option (context->pixbuf, "x-dpi", density_str);
				g_free (density_str);
				density_str = g_strdup_printf ("%d", DPCM_TO_DPI (cinfo->Y_density));
				gdk_pixbuf_set_option (context->pixbuf, "y-dpi", density_str);
				g_free (density_str);
				break;
			}

		        /* if orientation tag was found set an option to remember its value */
			if (exif_context.orientation != 0) {
				g_snprintf (otag_str, sizeof (otag_str), "%d", exif_context.orientation);
		                gdk_pixbuf_set_option (context->pixbuf, "orientation", otag_str);
		        }
			/* if icc profile was found */
			if (exif_context.icc_profile != NULL) {
				icc_profile_base64 = g_base64_encode ((const guchar *) exif_context.icc_profile, exif_context.icc_profile_size);
				gdk_pixbuf_set_option (context->pixbuf, "icc-profile", icc_profile_base64);
				g_free (icc_profile_base64);
			}

			/* Use pixbuf buffer to store decompressed data */
			context->dptr = context->pixbuf->pixels;

			/* Notify the client that we are ready to go */
			if (context->prepared_func)
				(* context->prepared_func) (context->pixbuf,
							    NULL,
							    context->user_data);

		} else if (!context->did_prescan) {
			int rc;

			/* start decompression */
			cinfo->buffered_image = cinfo->progressive_mode;
			rc = jpeg_start_decompress (cinfo);
			cinfo->do_fancy_upsampling = FALSE;
			cinfo->do_block_smoothing = FALSE;

			if (rc == JPEG_SUSPENDED)
				continue;

			context->did_prescan = TRUE;
		} else if (!cinfo->buffered_image) {
                        /* we're decompressing unbuffered so
                         * simply get scanline by scanline from jpeg lib
                         */
                        if (! gdk_pixbuf__jpeg_image_load_lines (context,
                                                                 error)) {
                                retval = FALSE;
				goto out;
			}

			if (cinfo->output_scanline >= cinfo->output_height) {
				retval = TRUE;
				goto out;
			}
		} else {
                        /* we're decompressing buffered (progressive)
                         * so feed jpeg lib scanlines
                         */

			/* keep going until we've done all passes */
			while (!jpeg_input_complete (cinfo)) {
				if (!context->in_output) {
					if (jpeg_start_output (cinfo, cinfo->input_scan_number)) {
						context->in_output = TRUE;
						context->dptr = context->pixbuf->pixels;
					}
					else
						break;
				}

                                /* get scanlines from jpeg lib */
                                if (! gdk_pixbuf__jpeg_image_load_lines (context,
                                                                         error)) {
                                        retval = FALSE;
					goto out;
				}

				if (cinfo->output_scanline >= cinfo->output_height &&
				    jpeg_finish_output (cinfo))
					context->in_output = FALSE;
				else
					break;
			}
			if (jpeg_input_complete (cinfo)) {
				/* did entire image */
				retval = TRUE;
				goto out;
			}
			else
				continue;
		}
	}
out:
	jpeg_destroy_exif_context (&exif_context);
	return retval;
}

An exploitable heap overflow vulnerability exists in the gdk_pixbuf__jpeg_image_load_increment functionality of Gdk-Pixbuf 2.36.6. A specially crafted jpeg file can cause a heap overflow resulting in remote code execution. An attacker can send a file or url to trigger this vulnerability.


gdk_pixbuf__jpeg_image_load_increment (gpointer data,
                                       const guchar *buf, guint size,
                                       GError **error)
{
	JpegProgContext *context = (JpegProgContext *)data;
	struct           jpeg_decompress_struct *cinfo;
	my_src_ptr       src;
	guint            num_left, num_copy;
	guint            last_num_left, last_bytes_left;
	guint            spinguard;
	gboolean         first;
	const guchar    *bufhd;
	gint             width, height;
	char             otag_str[5];
	gchar 		*icc_profile_base64;
	char            *density_str;
	JpegExifContext  exif_context = { 0, };
	gboolean	 retval;

	g_return_val_if_fail (context != NULL, FALSE);
	g_return_val_if_fail (buf != NULL, FALSE);

	src = (my_src_ptr) context->cinfo.src;

	cinfo = &context->cinfo;

        context->jerr.error = error;

	/* check for fatal error */
	if (sigsetjmp (context->jerr.setjmp_buffer, 1)) {
		retval = FALSE;
		goto out;
	}

	/* skip over data if requested, handle unsigned int sizes cleanly */
	/* only can happen if we've already called jpeg_get_header once   */
	if (context->src_initialized && src->skip_next) {
		if (src->skip_next > size) {
			src->skip_next -= size;
			retval = TRUE;
			goto out;
		} else {
			num_left = size - src->skip_next;
			bufhd = buf + src->skip_next;
			src->skip_next = 0;
		}
	} else {
		num_left = size;
		bufhd = buf;
	}

	if (num_left == 0) {
		retval = TRUE;
		goto out;
	}

	last_num_left = num_left;
	last_bytes_left = 0;
	spinguard = 0;
	first = TRUE;
	while (TRUE) {

		/* handle any data from caller we haven't processed yet */
		if (num_left > 0) {
			if(src->pub.bytes_in_buffer &&
			   src->pub.next_input_byte != src->buffer)
				memmove(src->buffer, src->pub.next_input_byte,
					src->pub.bytes_in_buffer);

			num_copy = MIN (JPEG_PROG_BUF_SIZE - src->pub.bytes_in_buffer,
					num_left);

			memcpy(src->buffer + src->pub.bytes_in_buffer, bufhd,num_copy);
			src->pub.next_input_byte = src->buffer;
			src->pub.bytes_in_buffer += num_copy;
			bufhd += num_copy;
			num_left -= num_copy;
		}

                /* did anything change from last pass, if not return */
                if (first) {
                        last_bytes_left = src->pub.bytes_in_buffer;
                        first = FALSE;
                } else if (src->pub.bytes_in_buffer == last_bytes_left
			   && num_left == last_num_left) {
                        spinguard++;
		} else {
                        last_bytes_left = src->pub.bytes_in_buffer;
			last_num_left = num_left;
		}

		/* should not go through twice and not pull bytes out of buf */
		if (spinguard > 2) {
			retval = TRUE;
			goto out;
		}

		/* try to load jpeg header */
		if (!context->got_header) {
			int rc;
			gchar* comment;
			gboolean has_alpha;

			jpeg_save_markers (cinfo, JPEG_APP0+1, 0xffff);
			jpeg_save_markers (cinfo, JPEG_APP0+2, 0xffff);
			jpeg_save_markers (cinfo, JPEG_COM, 0xffff);
			rc = jpeg_read_header (cinfo, TRUE);
			context->src_initialized = TRUE;

			if (rc == JPEG_SUSPENDED)
				continue;

			context->got_header = TRUE;

			/* parse exif data */
			jpeg_parse_exif (&exif_context, cinfo);

			width = cinfo->image_width;
			height = cinfo->image_height;
			if (context->size_func) {
				(* context->size_func) (&width, &height, context->user_data);
				if (width == 0 || height == 0) {
					g_set_error_literal (error,
                                                             GDK_PIXBUF_ERROR,
                                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                                             _("Transformed JPEG has zero width or height."));
					retval = FALSE;
					goto out;
				}
			}

			cinfo->scale_num = 1;
			for (cinfo->scale_denom = 2; cinfo->scale_denom <= 8; cinfo->scale_denom *= 2) {
				jpeg_calc_output_dimensions (cinfo);
				if (cinfo->output_width < width || cinfo->output_height < height) {
					cinfo->scale_denom /= 2;
					break;
				}
			}
			jpeg_calc_output_dimensions (cinfo);

			if (cinfo->output_components == 3) {
				has_alpha = FALSE;
			} else if (cinfo->output_components == 4) {
				has_alpha = TRUE;
			} else {
				g_set_error (error,
					     GDK_PIXBUF_ERROR,
					     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
					     _("Unsupported number of color components (%d)"),
					     cinfo->output_components);
				retval = FALSE;
				goto out;
			}

			context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
							  has_alpha,
							  8,
							  cinfo->output_width,
							  cinfo->output_height);

			if (context->pixbuf == NULL) {
                                g_set_error_literal (error,
                                                     GDK_PIXBUF_ERROR,
                                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                                     _("Couldn't allocate memory for loading JPEG file"));
                                retval = FALSE;
				goto out;
			}

			comment = jpeg_get_comment (cinfo);
			if (comment != NULL) {
				gdk_pixbuf_set_option (context->pixbuf, "comment", comment);
				g_free (comment);
			}

			switch (cinfo->density_unit) {
			case 1:
				/* Dots per inch (no conversion required) */
				density_str = g_strdup_printf ("%d", cinfo->X_density);
				gdk_pixbuf_set_option (context->pixbuf, "x-dpi", density_str);
				g_free (density_str);
				density_str = g_strdup_printf ("%d", cinfo->Y_density);
				gdk_pixbuf_set_option (context->pixbuf, "y-dpi", density_str);
				g_free (density_str);
				break;
			case 2:
				/* Dots per cm - convert into dpi */
				density_str = g_strdup_printf ("%d", DPCM_TO_DPI (cinfo->X_density));
				gdk_pixbuf_set_option (context->pixbuf, "x-dpi", density_str);
				g_free (density_str);
				density_str = g_strdup_printf ("%d", DPCM_TO_DPI (cinfo->Y_density));
				gdk_pixbuf_set_option (context->pixbuf, "y-dpi", density_str);
				g_free (density_str);
				break;
			}

		        /* if orientation tag was found set an option to remember its value */
			if (exif_context.orientation != 0) {
				g_snprintf (otag_str, sizeof (otag_str), "%d", exif_context.orientation);
		                gdk_pixbuf_set_option (context->pixbuf, "orientation", otag_str);
		        }
			/* if icc profile was found */
			if (exif_context.icc_profile != NULL) {
				icc_profile_base64 = g_base64_encode ((const guchar *) exif_context.icc_profile, exif_context.icc_profile_size);
				gdk_pixbuf_set_option (context->pixbuf, "icc-profile", icc_profile_base64);
				g_free (icc_profile_base64);
			}

			/* Use pixbuf buffer to store decompressed data */
			context->dptr = context->pixbuf->pixels;

			/* Notify the client that we are ready to go */
			if (context->prepared_func)
				(* context->prepared_func) (context->pixbuf,
							    NULL,
							    context->user_data);

		} else if (!context->did_prescan) {
			int rc;

			/* start decompression */
			cinfo->buffered_image = cinfo->progressive_mode;
			rc = jpeg_start_decompress (cinfo);
			cinfo->do_fancy_upsampling = FALSE;
			cinfo->do_block_smoothing = FALSE;

			if (rc == JPEG_SUSPENDED)
				continue;

			context->did_prescan = TRUE;
		} else if (!cinfo->buffered_image) {
                        /* we're decompressing unbuffered so
                         * simply get scanline by scanline from jpeg lib
                         */
                        if (! gdk_pixbuf__jpeg_image_load_lines (context,
                                                                 error)) {
                                retval = FALSE;
				goto out;
			}

			if (cinfo->output_scanline >= cinfo->output_height) {
				retval = TRUE;
				goto out;
			}
		} else {
                        /* we're decompressing buffered (progressive)
                         * so feed jpeg lib scanlines
                         */

			/* keep going until we've done all passes */
			while (!jpeg_input_complete (cinfo)) {
				if (!context->in_output) {
					if (jpeg_start_output (cinfo, cinfo->input_scan_number)) {
						context->in_output = TRUE;
						context->dptr = context->pixbuf->pixels;
					}
					else
						break;
				}

                                /* get scanlines from jpeg lib */
                                if (! gdk_pixbuf__jpeg_image_load_lines (context,
                                                                         error)) {
                                        retval = FALSE;
					goto out;
				}

				if (cinfo->output_scanline >= cinfo->output_height &&
				    jpeg_finish_output (cinfo))
					context->in_output = FALSE;
				else
					break;
			}
			if (jpeg_input_complete (cinfo)) {
				/* did entire image */
				retval = TRUE;
				goto out;
			}
			else
				continue;
		}
	}
out:
	jpeg_destroy_exif_context (&exif_context);
	return retval;
}


Do not read beyond end of escaped string

Credit to OSS-Fuzz

static char *theme_format_expand_abstract(THEME_REC *theme, const char **formatp,
                                          theme_rm_col *last_fg, theme_rm_col *last_bg, int flags,
                                          GTree *block_list)
{
	GString *str;
	const char *p, *format;
	char *abstract, *data, *ret;
	theme_rm_col default_fg, default_bg;
	int len;

	format = *formatp;
	default_fg = *last_fg;
	default_bg = *last_bg;

	/* get abstract name first */
	p = format;
	while (*p != '\0' && *p != ' ' &&
	       *p != '{' && *p != '}') p++;
	if (*p == '\0' || p == format)
		return NULL; /* error */

	len = (int) (p-format);
	abstract = g_strndup(format, len);

	/* skip the following space, if there's any more spaces they're
	   treated as arguments */
	if (*p == ' ') {
		len++;
		if ((flags & EXPAND_FLAG_IGNORE_EMPTY) && data_is_empty(&p)) {
			*formatp = p;
			g_free(abstract);
			return NULL;
		}
	}
	*formatp = format+len;

	if (block_list == NULL) {
		block_list = g_tree_new_full((GCompareDataFunc) g_strcmp0, NULL, g_free, NULL);
	} else {
		g_tree_ref(block_list);
	}

	/* get the abstract data */
	data = g_hash_table_lookup(theme->abstracts, abstract);
	if (data == NULL || g_tree_lookup(block_list, abstract) != NULL) {
		/* unknown abstract, just display the data */
		data = "$0-";
		g_free(abstract);
	} else {
		g_tree_insert(block_list, abstract, abstract);
	}
	abstract = g_strdup(data);

	/* we'll need to get the data part. it may contain
	   more abstracts, they are _NOT_ expanded. */
	data = theme_format_expand_get(theme, formatp);
	len = strlen(data);

	if (len > 1 && i_isdigit(data[len-1]) && data[len-2] == '$') {
		/* ends with $<digit> .. this breaks things if next
		   character is digit or '-' */
                char digit, *tmp;

		tmp = data;
		digit = tmp[len-1];
		tmp[len-1] = '\0';

		data = g_strdup_printf("%s{%c}", tmp, digit);
		g_free(tmp);
	}

	ret = parse_special_string(abstract, NULL, NULL, data, NULL,
				   PARSE_FLAG_ONLY_ARGS);
	g_free(abstract);
        g_free(data);
	str = g_string_new(NULL);
	p = ret;
	while (*p != '\0') {
		if (*p == '\\') {
			int chr;
			p++;
			chr = expand_escape(&p);
			g_string_append_c(str, chr != -1 ? chr : *p);
		} else
			g_string_append_c(str, *p);
		p++;
	}
	g_free(ret);
	abstract = str->str;
	g_string_free(str, FALSE);

	/* abstract may itself contain abstracts or replaces */
	p = abstract;
	ret = theme_format_expand_data_rec(theme, &p, default_fg, default_bg, last_fg, last_bg,
	                                   flags | EXPAND_FLAG_LASTCOLOR_ARG, block_list);
	g_free(abstract);
	g_tree_unref(block_list);
	return ret;
}

An issue was discovered in Irssi before 1.0.7 and 1.1.x before 1.1.1. Certain nick names could result in out-of-bounds access when printing theme strings.


static char *theme_format_expand_abstract(THEME_REC *theme, const char **formatp,
                                          theme_rm_col *last_fg, theme_rm_col *last_bg, int flags,
                                          GTree *block_list)
{
	GString *str;
	const char *p, *format;
	char *abstract, *data, *ret;
	theme_rm_col default_fg, default_bg;
	int len;

	format = *formatp;
	default_fg = *last_fg;
	default_bg = *last_bg;

	/* get abstract name first */
	p = format;
	while (*p != '\0' && *p != ' ' &&
	       *p != '{' && *p != '}') p++;
	if (*p == '\0' || p == format)
		return NULL; /* error */

	len = (int) (p-format);
	abstract = g_strndup(format, len);

	/* skip the following space, if there's any more spaces they're
	   treated as arguments */
	if (*p == ' ') {
		len++;
		if ((flags & EXPAND_FLAG_IGNORE_EMPTY) && data_is_empty(&p)) {
			*formatp = p;
			g_free(abstract);
			return NULL;
		}
	}
	*formatp = format+len;

	if (block_list == NULL) {
		block_list = g_tree_new_full((GCompareDataFunc) g_strcmp0, NULL, g_free, NULL);
	} else {
		g_tree_ref(block_list);
	}

	/* get the abstract data */
	data = g_hash_table_lookup(theme->abstracts, abstract);
	if (data == NULL || g_tree_lookup(block_list, abstract) != NULL) {
		/* unknown abstract, just display the data */
		data = "$0-";
		g_free(abstract);
	} else {
		g_tree_insert(block_list, abstract, abstract);
	}
	abstract = g_strdup(data);

	/* we'll need to get the data part. it may contain
	   more abstracts, they are _NOT_ expanded. */
	data = theme_format_expand_get(theme, formatp);
	len = strlen(data);

	if (len > 1 && i_isdigit(data[len-1]) && data[len-2] == '$') {
		/* ends with $<digit> .. this breaks things if next
		   character is digit or '-' */
                char digit, *tmp;

		tmp = data;
		digit = tmp[len-1];
		tmp[len-1] = '\0';

		data = g_strdup_printf("%s{%c}", tmp, digit);
		g_free(tmp);
	}

	ret = parse_special_string(abstract, NULL, NULL, data, NULL,
				   PARSE_FLAG_ONLY_ARGS);
	g_free(abstract);
        g_free(data);
	str = g_string_new(NULL);
	p = ret;
	while (*p != '\0') {
		if (*p == '\\' && p[1] != '\0') {
			int chr;
			p++;
			chr = expand_escape(&p);
			g_string_append_c(str, chr != -1 ? chr : *p);
		} else
			g_string_append_c(str, *p);
		p++;
	}
	g_free(ret);
	abstract = str->str;
	g_string_free(str, FALSE);

	/* abstract may itself contain abstracts or replaces */
	p = abstract;
	ret = theme_format_expand_data_rec(theme, &p, default_fg, default_bg, last_fg, last_bg,
	                                   flags | EXPAND_FLAG_LASTCOLOR_ARG, block_list);
	g_free(abstract);
	g_tree_unref(block_list);
	return ret;
}


umount: sanitize paths from non-root users

Signed-off-by: Karel Zak <kzak@redhat.com>

int main(int argc, char **argv)
{
	int c, rc = 0, all = 0, recursive = 0;
	struct libmnt_context *cxt;
	char *types = NULL;

	enum {
		UMOUNT_OPT_FAKE = CHAR_MAX + 1,
	};

	static const struct option longopts[] = {
		{ "all", 0, 0, 'a' },
		{ "detach-loop", 0, 0, 'd' },
		{ "fake", 0, 0, UMOUNT_OPT_FAKE },
		{ "force", 0, 0, 'f' },
		{ "help", 0, 0, 'h' },
		{ "internal-only", 0, 0, 'i' },
		{ "lazy", 0, 0, 'l' },
		{ "no-canonicalize", 0, 0, 'c' },
		{ "no-mtab", 0, 0, 'n' },
		{ "read-only", 0, 0, 'r' },
		{ "recursive", 0, 0, 'R' },
		{ "test-opts", 1, 0, 'O' },
		{ "types", 1, 0, 't' },
		{ "verbose", 0, 0, 'v' },
		{ "version", 0, 0, 'V' },
		{ NULL, 0, 0, 0 }
	};

	static const ul_excl_t excl[] = {       /* rows and cols in in ASCII order */
		{ 'R','a' },			/* recursive,all */
		{ 'O','R','t'},			/* options,recursive,types */
		{ 'R','r' },			/* recursive,read-only */
		{ 0 }
	};
	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;

	sanitize_env();
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
	atexit(close_stdout);

	mnt_init_debug(0);
	cxt = mnt_new_context();
	if (!cxt)
		err(MOUNT_EX_SYSERR, _("libmount context allocation failed"));

	mnt_context_set_tables_errcb(cxt, table_parser_errcb);

	while ((c = getopt_long(argc, argv, "acdfhilnRrO:t:vV",
					longopts, NULL)) != -1) {

		/* only few options are allowed for non-root users */
		if (mnt_context_is_restricted(cxt) && !strchr("hdilVv", c))
			exit_non_root(option_to_longopt(c, longopts));

		err_exclusive_options(c, longopts, excl, excl_st);

		switch(c) {
		case 'a':
			all = 1;
			break;
		case 'c':
			mnt_context_disable_canonicalize(cxt, TRUE);
			break;
		case 'd':
			mnt_context_enable_loopdel(cxt, TRUE);
			break;
		case UMOUNT_OPT_FAKE:
			mnt_context_enable_fake(cxt, TRUE);
			break;
		case 'f':
			mnt_context_enable_force(cxt, TRUE);
			break;
		case 'h':
			usage(stdout);
			break;
		case 'i':
			mnt_context_disable_helpers(cxt, TRUE);
			break;
		case 'l':
			mnt_context_enable_lazy(cxt, TRUE);
			break;
		case 'n':
			mnt_context_disable_mtab(cxt, TRUE);
			break;
		case 'r':
			mnt_context_enable_rdonly_umount(cxt, TRUE);
			break;
		case 'R':
			recursive = TRUE;
			break;
		case 'O':
			if (mnt_context_set_options_pattern(cxt, optarg))
				err(MOUNT_EX_SYSERR, _("failed to set options pattern"));
			break;
		case 't':
			types = optarg;
			break;
		case 'v':
			mnt_context_enable_verbose(cxt, TRUE);
			break;
		case 'V':
			print_version();
			break;
		default:
			usage(stderr);
			break;
		}
	}

	argc -= optind;
	argv += optind;

	if (all) {
		if (!types)
			types = "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd";

		mnt_context_set_fstype_pattern(cxt, types);
		rc = umount_all(cxt);

	} else if (argc < 1) {
		usage(stderr);

	} else if (recursive) {
		while (argc--)
			rc += umount_recursive(cxt, *argv++);
	} else {
		while (argc--)
			rc += umount_one(cxt, *argv++);
	}

	mnt_free_context(cxt);
	return rc;
}

(a) mount and (b) umount in util-linux 2.14.1, 2.17.2, and probably other versions allow local users to determine the existence of restricted directories by (1) using the --guess-fstype command-line option or (2) attempting to mount a non-existent device, which generates different error messages depending on whether the directory exists.


int main(int argc, char **argv)
{
	int c, rc = 0, all = 0, recursive = 0;
	struct libmnt_context *cxt;
	char *types = NULL;

	enum {
		UMOUNT_OPT_FAKE = CHAR_MAX + 1,
	};

	static const struct option longopts[] = {
		{ "all", 0, 0, 'a' },
		{ "detach-loop", 0, 0, 'd' },
		{ "fake", 0, 0, UMOUNT_OPT_FAKE },
		{ "force", 0, 0, 'f' },
		{ "help", 0, 0, 'h' },
		{ "internal-only", 0, 0, 'i' },
		{ "lazy", 0, 0, 'l' },
		{ "no-canonicalize", 0, 0, 'c' },
		{ "no-mtab", 0, 0, 'n' },
		{ "read-only", 0, 0, 'r' },
		{ "recursive", 0, 0, 'R' },
		{ "test-opts", 1, 0, 'O' },
		{ "types", 1, 0, 't' },
		{ "verbose", 0, 0, 'v' },
		{ "version", 0, 0, 'V' },
		{ NULL, 0, 0, 0 }
	};

	static const ul_excl_t excl[] = {       /* rows and cols in in ASCII order */
		{ 'R','a' },			/* recursive,all */
		{ 'O','R','t'},			/* options,recursive,types */
		{ 'R','r' },			/* recursive,read-only */
		{ 0 }
	};
	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;

	sanitize_env();
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
	atexit(close_stdout);

	mnt_init_debug(0);
	cxt = mnt_new_context();
	if (!cxt)
		err(MOUNT_EX_SYSERR, _("libmount context allocation failed"));

	mnt_context_set_tables_errcb(cxt, table_parser_errcb);

	while ((c = getopt_long(argc, argv, "acdfhilnRrO:t:vV",
					longopts, NULL)) != -1) {

		/* only few options are allowed for non-root users */
		if (mnt_context_is_restricted(cxt) && !strchr("hdilVv", c))
			exit_non_root(option_to_longopt(c, longopts));

		err_exclusive_options(c, longopts, excl, excl_st);

		switch(c) {
		case 'a':
			all = 1;
			break;
		case 'c':
			mnt_context_disable_canonicalize(cxt, TRUE);
			break;
		case 'd':
			mnt_context_enable_loopdel(cxt, TRUE);
			break;
		case UMOUNT_OPT_FAKE:
			mnt_context_enable_fake(cxt, TRUE);
			break;
		case 'f':
			mnt_context_enable_force(cxt, TRUE);
			break;
		case 'h':
			usage(stdout);
			break;
		case 'i':
			mnt_context_disable_helpers(cxt, TRUE);
			break;
		case 'l':
			mnt_context_enable_lazy(cxt, TRUE);
			break;
		case 'n':
			mnt_context_disable_mtab(cxt, TRUE);
			break;
		case 'r':
			mnt_context_enable_rdonly_umount(cxt, TRUE);
			break;
		case 'R':
			recursive = TRUE;
			break;
		case 'O':
			if (mnt_context_set_options_pattern(cxt, optarg))
				err(MOUNT_EX_SYSERR, _("failed to set options pattern"));
			break;
		case 't':
			types = optarg;
			break;
		case 'v':
			mnt_context_enable_verbose(cxt, TRUE);
			break;
		case 'V':
			print_version();
			break;
		default:
			usage(stderr);
			break;
		}
	}

	argc -= optind;
	argv += optind;

	if (all) {
		if (!types)
			types = "noproc,nodevfs,nodevpts,nosysfs,norpc_pipefs,nonfsd";

		mnt_context_set_fstype_pattern(cxt, types);
		rc = umount_all(cxt);

	} else if (argc < 1) {
		usage(stderr);

	} else if (recursive) {
		while (argc--)
			rc += umount_recursive(cxt, *argv++);
	} else {
		while (argc--) {
			char *path = *argv++;

			if (mnt_context_is_restricted(cxt))
				path = sanitize_path(path);

			rc += umount_one(cxt, path);

			if (mnt_context_is_restricted(cxt))
				free(path);
		}
	}

	mnt_free_context(cxt);
	return rc;
}


riff-media: Don't divide block align by zero channels

https://bugzilla.gnome.org/show_bug.cgi?id=777525

gst_riff_create_audio_caps (guint16 codec_id,
    gst_riff_strh * strh, gst_riff_strf_auds * strf,
    GstBuffer * strf_data, GstBuffer * strd_data, char **codec_name,
    gint channel_reorder_map[18])
{
  gboolean block_align = FALSE, rate_chan = TRUE;
  GstCaps *caps = NULL;
  gint i;

  if (channel_reorder_map)
    for (i = 0; i < 18; i++)
      channel_reorder_map[i] = -1;

  switch (codec_id) {
    case GST_RIFF_WAVE_FORMAT_PCM:     /* PCM */
      if (strf != NULL) {
        gint ba = strf->blockalign;
        gint ch = strf->channels;
        gint wd, ws;
        GstAudioFormat format;

        if (ba > (32 / 8) * ch) {
          GST_WARNING ("Invalid block align: %d > %d", ba, (32 / 8) * ch);
          wd = GST_ROUND_UP_8 (strf->bits_per_sample);
        } else if (ba != 0) {
          /* If we have an empty blockalign, we take the width contained in
           * strf->bits_per_sample */
          wd = ba * 8 / ch;
        } else {
          wd = GST_ROUND_UP_8 (strf->bits_per_sample);
        }

        if (strf->bits_per_sample > 32) {
          GST_WARNING ("invalid depth (%d) of pcm audio, overwriting.",
              strf->bits_per_sample);
          strf->bits_per_sample = wd;
        }

        /* in riff, the depth is stored in the size field but it just means that
         * the _least_ significant bits are cleared. We can therefore just play
         * the sample as if it had a depth == width */
        /* For reference, the actual depth is in strf->bits_per_sample */
        ws = wd;

        format =
            gst_audio_format_build_integer (wd != 8, G_LITTLE_ENDIAN, wd, ws);
        if (format == GST_AUDIO_FORMAT_UNKNOWN) {
          GST_WARNING ("Unsupported raw audio format with width %d", wd);
          return NULL;
        }

        caps = gst_caps_new_simple ("audio/x-raw",
            "format", G_TYPE_STRING, gst_audio_format_to_string (format),
            "layout", G_TYPE_STRING, "interleaved",
            "channels", G_TYPE_INT, ch, NULL);

        /* Add default channel layout. We know no default layout for more than
         * 8 channels. */
        if (ch > 8)
          GST_WARNING ("don't know default layout for %d channels", ch);
        else if (gst_riff_wave_add_default_channel_mask (caps, ch,
                channel_reorder_map))
          GST_DEBUG ("using default channel layout for %d channels", ch);
        else
          GST_WARNING ("failed to add channel layout");
      } else {
        /* FIXME: this is pretty useless - we need fixed caps */
        caps = gst_caps_from_string ("audio/x-raw, "
            "format = (string) { S8, U8, S16LE, U16LE, S24LE, "
            "U24LE, S32LE, U32LE }, " "layout = (string) interleaved");
      }
      if (codec_name && strf)
        *codec_name = g_strdup_printf ("Uncompressed %d-bit PCM audio",
            strf->bits_per_sample);
      break;

    case GST_RIFF_WAVE_FORMAT_ADPCM:
      if (strf != NULL) {
        /* Many encoding tools create a wrong bitrate information in the header,
         * so either we calculate the bitrate or mark it as invalid as this
         * would probably confuse timing */
        strf->av_bps = 0;
        if (strf->channels != 0 && strf->rate != 0 && strf->blockalign != 0) {
          int spb = ((strf->blockalign - strf->channels * 7) / 2) * 2;
          strf->av_bps =
              gst_util_uint64_scale_int (strf->rate, strf->blockalign, spb);
          GST_DEBUG ("fixing av_bps to calculated value %d of MS ADPCM",
              strf->av_bps);
        }
      }
      caps = gst_caps_new_simple ("audio/x-adpcm",
          "layout", G_TYPE_STRING, "microsoft", NULL);
      if (codec_name)
        *codec_name = g_strdup ("ADPCM audio");
      block_align = TRUE;
      break;

    case GST_RIFF_WAVE_FORMAT_IEEE_FLOAT:
      if (strf != NULL) {
        gint ba = strf->blockalign;
        gint ch = strf->channels;
        gint wd = ba * 8 / ch;

        caps = gst_caps_new_simple ("audio/x-raw",
            "format", G_TYPE_STRING, wd == 64 ? "F64LE" : "F32LE",
            "layout", G_TYPE_STRING, "interleaved",
            "channels", G_TYPE_INT, ch, NULL);

        /* Add default channel layout. We know no default layout for more than
         * 8 channels. */
        if (ch > 8)
          GST_WARNING ("don't know default layout for %d channels", ch);
        else if (gst_riff_wave_add_default_channel_mask (caps, ch,
                channel_reorder_map))
          GST_DEBUG ("using default channel layout for %d channels", ch);
        else
          GST_WARNING ("failed to add channel layout");
      } else {
        /* FIXME: this is pretty useless - we need fixed caps */
        caps = gst_caps_from_string ("audio/x-raw, "
            "format = (string) { F32LE, F64LE }, "
            "layout = (string) interleaved");
      }
      if (codec_name && strf)
        *codec_name = g_strdup_printf ("Uncompressed %d-bit IEEE float audio",
            strf->bits_per_sample);
      break;

    case GST_RIFF_WAVE_FORMAT_IBM_CVSD:
      goto unknown;

    case GST_RIFF_WAVE_FORMAT_ALAW:
      if (strf != NULL) {
        if (strf->bits_per_sample != 8) {
          GST_WARNING ("invalid depth (%d) of alaw audio, overwriting.",
              strf->bits_per_sample);
          strf->bits_per_sample = 8;
          strf->blockalign = (strf->bits_per_sample * strf->channels) / 8;
          strf->av_bps = strf->blockalign * strf->rate;
        }
        if (strf->av_bps == 0 || strf->blockalign == 0) {
          GST_WARNING ("fixing av_bps (%d) and blockalign (%d) of alaw audio",
              strf->av_bps, strf->blockalign);
          strf->blockalign = (strf->bits_per_sample * strf->channels) / 8;
          strf->av_bps = strf->blockalign * strf->rate;
        }
      }
      caps = gst_caps_new_empty_simple ("audio/x-alaw");
      if (codec_name)
        *codec_name = g_strdup ("A-law audio");
      break;

    case GST_RIFF_WAVE_FORMAT_WMS:
      caps = gst_caps_new_empty_simple ("audio/x-wms");
      if (strf != NULL) {
        gst_caps_set_simple (caps,
            "bitrate", G_TYPE_INT, strf->av_bps * 8,
            "width", G_TYPE_INT, strf->bits_per_sample,
            "depth", G_TYPE_INT, strf->bits_per_sample, NULL);
      } else {
        gst_caps_set_simple (caps,
            "bitrate", GST_TYPE_INT_RANGE, 0, G_MAXINT, NULL);
      }
      if (codec_name)
        *codec_name = g_strdup ("Windows Media Audio Speech");
      block_align = TRUE;
      break;

    case GST_RIFF_WAVE_FORMAT_MULAW:
      if (strf != NULL) {
        if (strf->bits_per_sample != 8) {
          GST_WARNING ("invalid depth (%d) of mulaw audio, overwriting.",
              strf->bits_per_sample);
          strf->bits_per_sample = 8;
          strf->blockalign = (strf->bits_per_sample * strf->channels) / 8;
          strf->av_bps = strf->blockalign * strf->rate;
        }
        if (strf->av_bps == 0 || strf->blockalign == 0) {
          GST_WARNING ("fixing av_bps (%d) and blockalign (%d) of mulaw audio",
              strf->av_bps, strf->blockalign);
          strf->blockalign = (strf->bits_per_sample * strf->channels) / 8;
          strf->av_bps = strf->blockalign * strf->rate;
        }
      }
      caps = gst_caps_new_empty_simple ("audio/x-mulaw");
      if (codec_name)
        *codec_name = g_strdup ("Mu-law audio");
      break;

    case GST_RIFF_WAVE_FORMAT_OKI_ADPCM:
      goto unknown;

    case GST_RIFF_WAVE_FORMAT_DVI_ADPCM:
      if (strf != NULL) {
        /* Many encoding tools create a wrong bitrate information in the
         * header, so either we calculate the bitrate or mark it as invalid
         * as this would probably confuse timing */
        strf->av_bps = 0;
        if (strf->channels != 0 && strf->rate != 0 && strf->blockalign != 0) {
          int spb = ((strf->blockalign - strf->channels * 4) / 2) * 2;
          strf->av_bps =
              gst_util_uint64_scale_int (strf->rate, strf->blockalign, spb);
          GST_DEBUG ("fixing av_bps to calculated value %d of IMA DVI ADPCM",
              strf->av_bps);
        }
      }
      caps = gst_caps_new_simple ("audio/x-adpcm",
          "layout", G_TYPE_STRING, "dvi", NULL);
      if (codec_name)
        *codec_name = g_strdup ("DVI ADPCM audio");
      block_align = TRUE;
      break;

    case GST_RIFF_WAVE_FORMAT_ADPCM_G722:
      caps = gst_caps_new_empty_simple ("audio/G722");
      if (codec_name)
        *codec_name = g_strdup ("G722 audio");
      break;

    case GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM:
      if (strf != NULL) {
        gint bitrate;
        bitrate = 0;
        if (strf->av_bps == 2000 || strf->av_bps == 3000 || strf->av_bps == 4000
            || strf->av_bps == 5000) {
          strf->blockalign = strf->av_bps / 1000;
          bitrate = strf->av_bps * 8;
        } else if (strf->blockalign >= 2 && strf->blockalign <= 5) {
          bitrate = strf->blockalign * 8000;
        }
        if (bitrate > 0) {
          caps = gst_caps_new_simple ("audio/x-adpcm",
              "layout", G_TYPE_STRING, "g726", "bitrate", G_TYPE_INT, bitrate,
              NULL);
        } else {
          caps = gst_caps_new_simple ("audio/x-adpcm",
              "layout", G_TYPE_STRING, "g726", NULL);
        }
      } else {
        caps = gst_caps_new_simple ("audio/x-adpcm",
            "layout", G_TYPE_STRING, "g726", NULL);
      }
      if (codec_name)
        *codec_name = g_strdup ("G726 ADPCM audio");
      block_align = TRUE;
      break;

    case GST_RIFF_WAVE_FORMAT_DSP_TRUESPEECH:
      caps = gst_caps_new_empty_simple ("audio/x-truespeech");
      if (codec_name)
        *codec_name = g_strdup ("DSP Group TrueSpeech");
      break;

    case GST_RIFF_WAVE_FORMAT_GSM610:
    case GST_RIFF_WAVE_FORMAT_MSN:
      caps = gst_caps_new_empty_simple ("audio/ms-gsm");
      if (codec_name)
        *codec_name = g_strdup ("MS GSM audio");
      break;

    case GST_RIFF_WAVE_FORMAT_MPEGL12: /* mp1 or mp2 */
      caps = gst_caps_new_simple ("audio/mpeg",
          "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 2, NULL);
      if (codec_name)
        *codec_name = g_strdup ("MPEG-1 layer 2");
      break;

    case GST_RIFF_WAVE_FORMAT_MPEGL3:  /* mp3 */
      caps = gst_caps_new_simple ("audio/mpeg",
          "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
      if (codec_name)
        *codec_name = g_strdup ("MPEG-1 layer 3");
      break;

    case GST_RIFF_WAVE_FORMAT_AMR_NB:  /* amr-nb */
      caps = gst_caps_new_empty_simple ("audio/AMR");
      if (codec_name)
        *codec_name = g_strdup ("AMR Narrow Band (NB)");
      break;

    case GST_RIFF_WAVE_FORMAT_AMR_WB:  /* amr-wb */
      caps = gst_caps_new_empty_simple ("audio/AMR-WB");
      if (codec_name)
        *codec_name = g_strdup ("AMR Wide Band (WB)");
      break;

    case GST_RIFF_WAVE_FORMAT_VORBIS1: /* ogg/vorbis mode 1 */
    case GST_RIFF_WAVE_FORMAT_VORBIS2: /* ogg/vorbis mode 2 */
    case GST_RIFF_WAVE_FORMAT_VORBIS3: /* ogg/vorbis mode 3 */
    case GST_RIFF_WAVE_FORMAT_VORBIS1PLUS:     /* ogg/vorbis mode 1+ */
    case GST_RIFF_WAVE_FORMAT_VORBIS2PLUS:     /* ogg/vorbis mode 2+ */
    case GST_RIFF_WAVE_FORMAT_VORBIS3PLUS:     /* ogg/vorbis mode 3+ */
      caps = gst_caps_new_empty_simple ("audio/x-vorbis");
      if (codec_name)
        *codec_name = g_strdup ("Vorbis");
      break;

    case GST_RIFF_WAVE_FORMAT_A52:
      caps = gst_caps_new_empty_simple ("audio/x-ac3");
      if (codec_name)
        *codec_name = g_strdup ("AC-3 audio");
      break;
    case GST_RIFF_WAVE_FORMAT_DTS:
      caps = gst_caps_new_empty_simple ("audio/x-dts");
      if (codec_name)
        *codec_name = g_strdup ("DTS audio");
      /* wavparse is not always able to specify rate/channels for DTS-in-wav */
      rate_chan = FALSE;
      break;
    case GST_RIFF_WAVE_FORMAT_AAC:
    case GST_RIFF_WAVE_FORMAT_AAC_AC:
    case GST_RIFF_WAVE_FORMAT_AAC_pm:
    {
      caps = gst_caps_new_simple ("audio/mpeg",
          "mpegversion", G_TYPE_INT, 4, NULL);
      if (codec_name)
        *codec_name = g_strdup ("MPEG-4 AAC audio");
      break;
    }
    case GST_RIFF_WAVE_FORMAT_WMAV1:
    case GST_RIFF_WAVE_FORMAT_WMAV2:
    case GST_RIFF_WAVE_FORMAT_WMAV3:
    case GST_RIFF_WAVE_FORMAT_WMAV3_L:
    {
      gint version = (codec_id - GST_RIFF_WAVE_FORMAT_WMAV1) + 1;

      block_align = TRUE;

      caps = gst_caps_new_simple ("audio/x-wma",
          "wmaversion", G_TYPE_INT, version, NULL);

      if (codec_name) {
        if (codec_id == GST_RIFF_WAVE_FORMAT_WMAV3_L)
          *codec_name = g_strdup ("WMA Lossless");
        else
          *codec_name = g_strdup_printf ("WMA Version %d", version + 6);
      }

      if (strf != NULL) {
        gst_caps_set_simple (caps,
            "bitrate", G_TYPE_INT, strf->av_bps * 8,
            "depth", G_TYPE_INT, strf->bits_per_sample, NULL);
      } else {
        gst_caps_set_simple (caps,
            "bitrate", GST_TYPE_INT_RANGE, 0, G_MAXINT, NULL);
      }
      break;
    }
    case GST_RIFF_WAVE_FORMAT_SONY_ATRAC3:
      caps = gst_caps_new_empty_simple ("audio/x-vnd.sony.atrac3");
      if (codec_name)
        *codec_name = g_strdup ("Sony ATRAC3");
      break;

    case GST_RIFF_WAVE_FORMAT_SIREN:
      caps = gst_caps_new_empty_simple ("audio/x-siren");
      if (codec_name)
        *codec_name = g_strdup ("Siren7");
      rate_chan = FALSE;
      break;

    case GST_RIFF_WAVE_FORMAT_ADPCM_IMA_DK4:
      caps =
          gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING, "dk4",
          NULL);
      if (codec_name)
        *codec_name = g_strdup ("IMA/DK4 ADPCM");
      break;
    case GST_RIFF_WAVE_FORMAT_ADPCM_IMA_DK3:
      caps =
          gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING, "dk3",
          NULL);
      if (codec_name)
        *codec_name = g_strdup ("IMA/DK3 ADPCM");
      break;

    case GST_RIFF_WAVE_FORMAT_ADPCM_IMA_WAV:
      caps =
          gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING, "dvi",
          NULL);
      if (codec_name)
        *codec_name = g_strdup ("IMA/WAV ADPCM");
      break;
    case GST_RIFF_WAVE_FORMAT_EXTENSIBLE:{
      guint16 valid_bits_per_sample;
      guint32 channel_mask;
      guint32 subformat_guid[4];
      GstMapInfo info;
      gsize size;

      /* should be at least 22 bytes */
      size = gst_buffer_get_size (strf_data);

      if (strf_data == NULL || size < 22) {
        GST_WARNING ("WAVE_FORMAT_EXTENSIBLE data size is %" G_GSIZE_FORMAT
            " (expected: 22)", (strf_data) ? size : -1);
        return NULL;
      }

      gst_buffer_map (strf_data, &info, GST_MAP_READ);
      valid_bits_per_sample = GST_READ_UINT16_LE (info.data);
      channel_mask = GST_READ_UINT32_LE (info.data + 2);
      subformat_guid[0] = GST_READ_UINT32_LE (info.data + 6);
      subformat_guid[1] = GST_READ_UINT32_LE (info.data + 10);
      subformat_guid[2] = GST_READ_UINT32_LE (info.data + 14);
      subformat_guid[3] = GST_READ_UINT32_LE (info.data + 18);
      gst_buffer_unmap (strf_data, &info);

      GST_DEBUG ("valid bps    = %u", valid_bits_per_sample);
      GST_DEBUG ("channel mask = 0x%08x", channel_mask);
      GST_DEBUG ("GUID         = %08x-%08x-%08x-%08x", subformat_guid[0],
          subformat_guid[1], subformat_guid[2], subformat_guid[3]);

      if (subformat_guid[1] == 0x00100000 &&
          subformat_guid[2] == 0xaa000080 && subformat_guid[3] == 0x719b3800) {
        if (subformat_guid[0] == 0x00000001) {
          GST_DEBUG ("PCM");
          if (strf != NULL && strf->blockalign != 0 && strf->channels != 0
              && strf->rate != 0) {
            gint ba = strf->blockalign;
            gint wd = ba * 8 / strf->channels;
            gint ws;
            GstAudioFormat format;

            /* in riff, the depth is stored in the size field but it just
             * means that the _least_ significant bits are cleared. We can
             * therefore just play the sample as if it had a depth == width */
            ws = wd;

            /* For reference, use this to get the actual depth:
             * ws = strf->bits_per_sample;
             * if (valid_bits_per_sample != 0)
             *   ws = valid_bits_per_sample; */

            format =
                gst_audio_format_build_integer (wd != 8, G_LITTLE_ENDIAN, wd,
                ws);

            caps = gst_caps_new_simple ("audio/x-raw",
                "format", G_TYPE_STRING, gst_audio_format_to_string (format),
                "layout", G_TYPE_STRING, "interleaved",
                "channels", G_TYPE_INT, strf->channels,
                "rate", G_TYPE_INT, strf->rate, NULL);

            if (codec_name) {
              *codec_name = g_strdup_printf ("Uncompressed %d-bit PCM audio",
                  strf->bits_per_sample);
            }
          }
        } else if (subformat_guid[0] == 0x00000003) {
          GST_DEBUG ("FLOAT");
          if (strf != NULL && strf->blockalign != 0 && strf->channels != 0
              && strf->rate != 0) {
            gint ba = strf->blockalign;
            gint wd = ba * 8 / strf->channels;

            caps = gst_caps_new_simple ("audio/x-raw",
                "format", G_TYPE_STRING, wd == 32 ? "F32LE" : "F64LE",
                "layout", G_TYPE_STRING, "interleaved",
                "channels", G_TYPE_INT, strf->channels,
                "rate", G_TYPE_INT, strf->rate, NULL);

            if (codec_name) {
              *codec_name =
                  g_strdup_printf ("Uncompressed %d-bit IEEE float audio",
                  strf->bits_per_sample);
            }
          }
        } else if (subformat_guid[0] == 0x0000006) {
          GST_DEBUG ("ALAW");
          if (strf != NULL) {
            if (strf->bits_per_sample != 8) {
              GST_WARNING ("invalid depth (%d) of alaw audio, overwriting.",
                  strf->bits_per_sample);
              strf->bits_per_sample = 8;
              strf->av_bps = 8;
              strf->blockalign = strf->av_bps * strf->channels;
            }
            if (strf->av_bps == 0 || strf->blockalign == 0) {
              GST_WARNING
                  ("fixing av_bps (%d) and blockalign (%d) of alaw audio",
                  strf->av_bps, strf->blockalign);
              strf->av_bps = strf->bits_per_sample;
              strf->blockalign = strf->av_bps * strf->channels;
            }
          }
          caps = gst_caps_new_empty_simple ("audio/x-alaw");

          if (codec_name)
            *codec_name = g_strdup ("A-law audio");
        } else if (subformat_guid[0] == 0x00000007) {
          GST_DEBUG ("MULAW");
          if (strf != NULL) {
            if (strf->bits_per_sample != 8) {
              GST_WARNING ("invalid depth (%d) of mulaw audio, overwriting.",
                  strf->bits_per_sample);
              strf->bits_per_sample = 8;
              strf->av_bps = 8;
              strf->blockalign = strf->av_bps * strf->channels;
            }
            if (strf->av_bps == 0 || strf->blockalign == 0) {
              GST_WARNING
                  ("fixing av_bps (%d) and blockalign (%d) of mulaw audio",
                  strf->av_bps, strf->blockalign);
              strf->av_bps = strf->bits_per_sample;
              strf->blockalign = strf->av_bps * strf->channels;
            }
          }
          caps = gst_caps_new_empty_simple ("audio/x-mulaw");
          if (codec_name)
            *codec_name = g_strdup ("Mu-law audio");
        } else if (subformat_guid[0] == 0x00000092) {
          GST_DEBUG ("FIXME: handle DOLBY AC3 SPDIF format");
          GST_DEBUG ("WAVE_FORMAT_EXTENSIBLE AC-3 SPDIF audio");
          caps = gst_caps_new_empty_simple ("audio/x-ac3");
          if (codec_name)
            *codec_name = g_strdup ("wavext AC-3 SPDIF audio");
        } else if ((subformat_guid[0] & 0xffff) ==
            GST_RIFF_WAVE_FORMAT_EXTENSIBLE) {
          GST_DEBUG ("WAVE_FORMAT_EXTENSIBLE nested");
        } else {
          /* recurse where no special consideration has yet to be identified
           * for the subformat guid */
          caps = gst_riff_create_audio_caps (subformat_guid[0], strh, strf,
              strf_data, strd_data, codec_name, channel_reorder_map);
          if (!codec_name)
            GST_DEBUG ("WAVE_FORMAT_EXTENSIBLE audio");
          if (caps) {
            if (codec_name) {
              GST_DEBUG ("WAVE_FORMAT_EXTENSIBLE %s", *codec_name);
              *codec_name = g_strjoin ("wavext ", *codec_name, NULL);
            }
            return caps;
          }
        }
      } else if (subformat_guid[0] == 0x6ba47966 &&
          subformat_guid[1] == 0x41783f83 &&
          subformat_guid[2] == 0xf0006596 && subformat_guid[3] == 0xe59262bf) {
        caps = gst_caps_new_empty_simple ("application/x-ogg-avi");
        if (codec_name)
          *codec_name = g_strdup ("Ogg-AVI");
      }

      if (caps == NULL) {
        GST_WARNING ("Unknown WAVE_FORMAT_EXTENSIBLE audio format");
        return NULL;
      }

      if (strf != NULL) {
        /* If channel_mask == 0 and channels > 1 let's
         * assume default layout as some wav files don't have the
         * channel mask set. Don't set the layout for 1 channel. */
        if (channel_mask == 0 && strf->channels > 1)
          channel_mask =
              gst_riff_wavext_get_default_channel_mask (strf->channels);

        if ((channel_mask != 0 || strf->channels > 1) &&
            !gst_riff_wavext_add_channel_mask (caps, strf->channels,
                channel_mask, channel_reorder_map)) {
          GST_WARNING ("failed to add channel layout");
          gst_caps_unref (caps);
          caps = NULL;
        }
        rate_chan = FALSE;
      }

      break;
    }
      /* can anything decode these? pitfdll? */
    case GST_RIFF_WAVE_FORMAT_VOXWARE_AC8:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_AC10:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_AC16:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_AC20:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_METAVOICE:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_METASOUND:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_RT29HW:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_VR12:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_VR18:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_TQ40:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_TQ60:{
      caps = gst_caps_new_simple ("audio/x-voxware",
          "voxwaretype", G_TYPE_INT, (gint) codec_id, NULL);
      if (codec_name)
        *codec_name = g_strdup ("Voxware");
      break;
    }
    default:
    unknown:
      GST_WARNING ("Unknown audio tag 0x%04x", codec_id);
      return NULL;
  }

  if (strf != NULL) {
    if (rate_chan) {
      gst_caps_set_simple (caps,
          "rate", G_TYPE_INT, strf->rate,
          "channels", G_TYPE_INT, strf->channels, NULL);
    }
    if (block_align) {
      gst_caps_set_simple (caps,
          "block_align", G_TYPE_INT, strf->blockalign, NULL);
    }
  } else {
    if (block_align) {
      gst_caps_set_simple (caps,
          "block_align", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
    }
  }

  /* extradata */
  if (strf_data || strd_data) {
    gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER,
        strf_data ? strf_data : strd_data, NULL);
  }

  return caps;
}

The gst_riff_create_audio_caps function in gst-libs/gst/riff/riff-media.c in gst-plugins-base in GStreamer before 1.10.3 allows remote attackers to cause a denial of service (floating point exception and crash) via a crafted ASF file.


gst_riff_create_audio_caps (guint16 codec_id,
    gst_riff_strh * strh, gst_riff_strf_auds * strf,
    GstBuffer * strf_data, GstBuffer * strd_data, char **codec_name,
    gint channel_reorder_map[18])
{
  gboolean block_align = FALSE, rate_chan = TRUE;
  GstCaps *caps = NULL;
  gint i;

  if (channel_reorder_map)
    for (i = 0; i < 18; i++)
      channel_reorder_map[i] = -1;

  switch (codec_id) {
    case GST_RIFF_WAVE_FORMAT_PCM:     /* PCM */
      if (strf != NULL) {
        gint ba = strf->blockalign;
        gint ch = strf->channels;
        gint wd, ws;
        GstAudioFormat format;

        if (ba > (32 / 8) * ch) {
          GST_WARNING ("Invalid block align: %d > %d", ba, (32 / 8) * ch);
          wd = GST_ROUND_UP_8 (strf->bits_per_sample);
        } else if (ba != 0) {
          /* If we have an empty blockalign, we take the width contained in
           * strf->bits_per_sample */
          wd = ba * 8 / ch;
        } else {
          wd = GST_ROUND_UP_8 (strf->bits_per_sample);
        }

        if (strf->bits_per_sample > 32) {
          GST_WARNING ("invalid depth (%d) of pcm audio, overwriting.",
              strf->bits_per_sample);
          strf->bits_per_sample = wd;
        }

        /* in riff, the depth is stored in the size field but it just means that
         * the _least_ significant bits are cleared. We can therefore just play
         * the sample as if it had a depth == width */
        /* For reference, the actual depth is in strf->bits_per_sample */
        ws = wd;

        format =
            gst_audio_format_build_integer (wd != 8, G_LITTLE_ENDIAN, wd, ws);
        if (format == GST_AUDIO_FORMAT_UNKNOWN) {
          GST_WARNING ("Unsupported raw audio format with width %d", wd);
          return NULL;
        }

        caps = gst_caps_new_simple ("audio/x-raw",
            "format", G_TYPE_STRING, gst_audio_format_to_string (format),
            "layout", G_TYPE_STRING, "interleaved",
            "channels", G_TYPE_INT, ch, NULL);

        /* Add default channel layout. We know no default layout for more than
         * 8 channels. */
        if (ch > 8)
          GST_WARNING ("don't know default layout for %d channels", ch);
        else if (gst_riff_wave_add_default_channel_mask (caps, ch,
                channel_reorder_map))
          GST_DEBUG ("using default channel layout for %d channels", ch);
        else
          GST_WARNING ("failed to add channel layout");
      } else {
        /* FIXME: this is pretty useless - we need fixed caps */
        caps = gst_caps_from_string ("audio/x-raw, "
            "format = (string) { S8, U8, S16LE, U16LE, S24LE, "
            "U24LE, S32LE, U32LE }, " "layout = (string) interleaved");
      }
      if (codec_name && strf)
        *codec_name = g_strdup_printf ("Uncompressed %d-bit PCM audio",
            strf->bits_per_sample);
      break;

    case GST_RIFF_WAVE_FORMAT_ADPCM:
      if (strf != NULL) {
        /* Many encoding tools create a wrong bitrate information in the header,
         * so either we calculate the bitrate or mark it as invalid as this
         * would probably confuse timing */
        strf->av_bps = 0;
        if (strf->channels != 0 && strf->rate != 0 && strf->blockalign != 0) {
          int spb = ((strf->blockalign - strf->channels * 7) / 2) * 2;
          strf->av_bps =
              gst_util_uint64_scale_int (strf->rate, strf->blockalign, spb);
          GST_DEBUG ("fixing av_bps to calculated value %d of MS ADPCM",
              strf->av_bps);
        }
      }
      caps = gst_caps_new_simple ("audio/x-adpcm",
          "layout", G_TYPE_STRING, "microsoft", NULL);
      if (codec_name)
        *codec_name = g_strdup ("ADPCM audio");
      block_align = TRUE;
      break;

    case GST_RIFF_WAVE_FORMAT_IEEE_FLOAT:
      if (strf != NULL) {
        gint ba = strf->blockalign;
        gint ch = strf->channels;

        if (ba > 0 && ch > 0 && (ba == (64 / 8) * ch || ba == (32 / 8) * ch)) {
          gint wd = ba * 8 / ch;

          caps = gst_caps_new_simple ("audio/x-raw",
              "format", G_TYPE_STRING, wd == 64 ? "F64LE" : "F32LE",
              "layout", G_TYPE_STRING, "interleaved",
              "channels", G_TYPE_INT, ch, NULL);

          /* Add default channel layout. We know no default layout for more than
           * 8 channels. */
          if (ch > 8)
            GST_WARNING ("don't know default layout for %d channels", ch);
          else if (gst_riff_wave_add_default_channel_mask (caps, ch,
                  channel_reorder_map))
            GST_DEBUG ("using default channel layout for %d channels", ch);
          else
            GST_WARNING ("failed to add channel layout");
        } else {
          GST_WARNING ("invalid block align %d or channel count %d", ba, ch);
          return NULL;
        }
      } else {
        /* FIXME: this is pretty useless - we need fixed caps */
        caps = gst_caps_from_string ("audio/x-raw, "
            "format = (string) { F32LE, F64LE }, "
            "layout = (string) interleaved");
      }
      if (codec_name && strf)
        *codec_name = g_strdup_printf ("Uncompressed %d-bit IEEE float audio",
            strf->bits_per_sample);
      break;

    case GST_RIFF_WAVE_FORMAT_IBM_CVSD:
      goto unknown;

    case GST_RIFF_WAVE_FORMAT_ALAW:
      if (strf != NULL) {
        if (strf->bits_per_sample != 8) {
          GST_WARNING ("invalid depth (%d) of alaw audio, overwriting.",
              strf->bits_per_sample);
          strf->bits_per_sample = 8;
          strf->blockalign = (strf->bits_per_sample * strf->channels) / 8;
          strf->av_bps = strf->blockalign * strf->rate;
        }
        if (strf->av_bps == 0 || strf->blockalign == 0) {
          GST_WARNING ("fixing av_bps (%d) and blockalign (%d) of alaw audio",
              strf->av_bps, strf->blockalign);
          strf->blockalign = (strf->bits_per_sample * strf->channels) / 8;
          strf->av_bps = strf->blockalign * strf->rate;
        }
      }
      caps = gst_caps_new_empty_simple ("audio/x-alaw");
      if (codec_name)
        *codec_name = g_strdup ("A-law audio");
      break;

    case GST_RIFF_WAVE_FORMAT_WMS:
      caps = gst_caps_new_empty_simple ("audio/x-wms");
      if (strf != NULL) {
        gst_caps_set_simple (caps,
            "bitrate", G_TYPE_INT, strf->av_bps * 8,
            "width", G_TYPE_INT, strf->bits_per_sample,
            "depth", G_TYPE_INT, strf->bits_per_sample, NULL);
      } else {
        gst_caps_set_simple (caps,
            "bitrate", GST_TYPE_INT_RANGE, 0, G_MAXINT, NULL);
      }
      if (codec_name)
        *codec_name = g_strdup ("Windows Media Audio Speech");
      block_align = TRUE;
      break;

    case GST_RIFF_WAVE_FORMAT_MULAW:
      if (strf != NULL) {
        if (strf->bits_per_sample != 8) {
          GST_WARNING ("invalid depth (%d) of mulaw audio, overwriting.",
              strf->bits_per_sample);
          strf->bits_per_sample = 8;
          strf->blockalign = (strf->bits_per_sample * strf->channels) / 8;
          strf->av_bps = strf->blockalign * strf->rate;
        }
        if (strf->av_bps == 0 || strf->blockalign == 0) {
          GST_WARNING ("fixing av_bps (%d) and blockalign (%d) of mulaw audio",
              strf->av_bps, strf->blockalign);
          strf->blockalign = (strf->bits_per_sample * strf->channels) / 8;
          strf->av_bps = strf->blockalign * strf->rate;
        }
      }
      caps = gst_caps_new_empty_simple ("audio/x-mulaw");
      if (codec_name)
        *codec_name = g_strdup ("Mu-law audio");
      break;

    case GST_RIFF_WAVE_FORMAT_OKI_ADPCM:
      goto unknown;

    case GST_RIFF_WAVE_FORMAT_DVI_ADPCM:
      if (strf != NULL) {
        /* Many encoding tools create a wrong bitrate information in the
         * header, so either we calculate the bitrate or mark it as invalid
         * as this would probably confuse timing */
        strf->av_bps = 0;
        if (strf->channels != 0 && strf->rate != 0 && strf->blockalign != 0) {
          int spb = ((strf->blockalign - strf->channels * 4) / 2) * 2;
          strf->av_bps =
              gst_util_uint64_scale_int (strf->rate, strf->blockalign, spb);
          GST_DEBUG ("fixing av_bps to calculated value %d of IMA DVI ADPCM",
              strf->av_bps);
        }
      }
      caps = gst_caps_new_simple ("audio/x-adpcm",
          "layout", G_TYPE_STRING, "dvi", NULL);
      if (codec_name)
        *codec_name = g_strdup ("DVI ADPCM audio");
      block_align = TRUE;
      break;

    case GST_RIFF_WAVE_FORMAT_ADPCM_G722:
      caps = gst_caps_new_empty_simple ("audio/G722");
      if (codec_name)
        *codec_name = g_strdup ("G722 audio");
      break;

    case GST_RIFF_WAVE_FORMAT_ITU_G726_ADPCM:
      if (strf != NULL) {
        gint bitrate;
        bitrate = 0;
        if (strf->av_bps == 2000 || strf->av_bps == 3000 || strf->av_bps == 4000
            || strf->av_bps == 5000) {
          strf->blockalign = strf->av_bps / 1000;
          bitrate = strf->av_bps * 8;
        } else if (strf->blockalign >= 2 && strf->blockalign <= 5) {
          bitrate = strf->blockalign * 8000;
        }
        if (bitrate > 0) {
          caps = gst_caps_new_simple ("audio/x-adpcm",
              "layout", G_TYPE_STRING, "g726", "bitrate", G_TYPE_INT, bitrate,
              NULL);
        } else {
          caps = gst_caps_new_simple ("audio/x-adpcm",
              "layout", G_TYPE_STRING, "g726", NULL);
        }
      } else {
        caps = gst_caps_new_simple ("audio/x-adpcm",
            "layout", G_TYPE_STRING, "g726", NULL);
      }
      if (codec_name)
        *codec_name = g_strdup ("G726 ADPCM audio");
      block_align = TRUE;
      break;

    case GST_RIFF_WAVE_FORMAT_DSP_TRUESPEECH:
      caps = gst_caps_new_empty_simple ("audio/x-truespeech");
      if (codec_name)
        *codec_name = g_strdup ("DSP Group TrueSpeech");
      break;

    case GST_RIFF_WAVE_FORMAT_GSM610:
    case GST_RIFF_WAVE_FORMAT_MSN:
      caps = gst_caps_new_empty_simple ("audio/ms-gsm");
      if (codec_name)
        *codec_name = g_strdup ("MS GSM audio");
      break;

    case GST_RIFF_WAVE_FORMAT_MPEGL12: /* mp1 or mp2 */
      caps = gst_caps_new_simple ("audio/mpeg",
          "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 2, NULL);
      if (codec_name)
        *codec_name = g_strdup ("MPEG-1 layer 2");
      break;

    case GST_RIFF_WAVE_FORMAT_MPEGL3:  /* mp3 */
      caps = gst_caps_new_simple ("audio/mpeg",
          "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
      if (codec_name)
        *codec_name = g_strdup ("MPEG-1 layer 3");
      break;

    case GST_RIFF_WAVE_FORMAT_AMR_NB:  /* amr-nb */
      caps = gst_caps_new_empty_simple ("audio/AMR");
      if (codec_name)
        *codec_name = g_strdup ("AMR Narrow Band (NB)");
      break;

    case GST_RIFF_WAVE_FORMAT_AMR_WB:  /* amr-wb */
      caps = gst_caps_new_empty_simple ("audio/AMR-WB");
      if (codec_name)
        *codec_name = g_strdup ("AMR Wide Band (WB)");
      break;

    case GST_RIFF_WAVE_FORMAT_VORBIS1: /* ogg/vorbis mode 1 */
    case GST_RIFF_WAVE_FORMAT_VORBIS2: /* ogg/vorbis mode 2 */
    case GST_RIFF_WAVE_FORMAT_VORBIS3: /* ogg/vorbis mode 3 */
    case GST_RIFF_WAVE_FORMAT_VORBIS1PLUS:     /* ogg/vorbis mode 1+ */
    case GST_RIFF_WAVE_FORMAT_VORBIS2PLUS:     /* ogg/vorbis mode 2+ */
    case GST_RIFF_WAVE_FORMAT_VORBIS3PLUS:     /* ogg/vorbis mode 3+ */
      caps = gst_caps_new_empty_simple ("audio/x-vorbis");
      if (codec_name)
        *codec_name = g_strdup ("Vorbis");
      break;

    case GST_RIFF_WAVE_FORMAT_A52:
      caps = gst_caps_new_empty_simple ("audio/x-ac3");
      if (codec_name)
        *codec_name = g_strdup ("AC-3 audio");
      break;
    case GST_RIFF_WAVE_FORMAT_DTS:
      caps = gst_caps_new_empty_simple ("audio/x-dts");
      if (codec_name)
        *codec_name = g_strdup ("DTS audio");
      /* wavparse is not always able to specify rate/channels for DTS-in-wav */
      rate_chan = FALSE;
      break;
    case GST_RIFF_WAVE_FORMAT_AAC:
    case GST_RIFF_WAVE_FORMAT_AAC_AC:
    case GST_RIFF_WAVE_FORMAT_AAC_pm:
    {
      caps = gst_caps_new_simple ("audio/mpeg",
          "mpegversion", G_TYPE_INT, 4, NULL);
      if (codec_name)
        *codec_name = g_strdup ("MPEG-4 AAC audio");
      break;
    }
    case GST_RIFF_WAVE_FORMAT_WMAV1:
    case GST_RIFF_WAVE_FORMAT_WMAV2:
    case GST_RIFF_WAVE_FORMAT_WMAV3:
    case GST_RIFF_WAVE_FORMAT_WMAV3_L:
    {
      gint version = (codec_id - GST_RIFF_WAVE_FORMAT_WMAV1) + 1;

      block_align = TRUE;

      caps = gst_caps_new_simple ("audio/x-wma",
          "wmaversion", G_TYPE_INT, version, NULL);

      if (codec_name) {
        if (codec_id == GST_RIFF_WAVE_FORMAT_WMAV3_L)
          *codec_name = g_strdup ("WMA Lossless");
        else
          *codec_name = g_strdup_printf ("WMA Version %d", version + 6);
      }

      if (strf != NULL) {
        gst_caps_set_simple (caps,
            "bitrate", G_TYPE_INT, strf->av_bps * 8,
            "depth", G_TYPE_INT, strf->bits_per_sample, NULL);
      } else {
        gst_caps_set_simple (caps,
            "bitrate", GST_TYPE_INT_RANGE, 0, G_MAXINT, NULL);
      }
      break;
    }
    case GST_RIFF_WAVE_FORMAT_SONY_ATRAC3:
      caps = gst_caps_new_empty_simple ("audio/x-vnd.sony.atrac3");
      if (codec_name)
        *codec_name = g_strdup ("Sony ATRAC3");
      break;

    case GST_RIFF_WAVE_FORMAT_SIREN:
      caps = gst_caps_new_empty_simple ("audio/x-siren");
      if (codec_name)
        *codec_name = g_strdup ("Siren7");
      rate_chan = FALSE;
      break;

    case GST_RIFF_WAVE_FORMAT_ADPCM_IMA_DK4:
      caps =
          gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING, "dk4",
          NULL);
      if (codec_name)
        *codec_name = g_strdup ("IMA/DK4 ADPCM");
      break;
    case GST_RIFF_WAVE_FORMAT_ADPCM_IMA_DK3:
      caps =
          gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING, "dk3",
          NULL);
      if (codec_name)
        *codec_name = g_strdup ("IMA/DK3 ADPCM");
      break;

    case GST_RIFF_WAVE_FORMAT_ADPCM_IMA_WAV:
      caps =
          gst_caps_new_simple ("audio/x-adpcm", "layout", G_TYPE_STRING, "dvi",
          NULL);
      if (codec_name)
        *codec_name = g_strdup ("IMA/WAV ADPCM");
      break;
    case GST_RIFF_WAVE_FORMAT_EXTENSIBLE:{
      guint16 valid_bits_per_sample;
      guint32 channel_mask;
      guint32 subformat_guid[4];
      GstMapInfo info;
      gsize size;

      /* should be at least 22 bytes */
      size = gst_buffer_get_size (strf_data);

      if (strf_data == NULL || size < 22) {
        GST_WARNING ("WAVE_FORMAT_EXTENSIBLE data size is %" G_GSIZE_FORMAT
            " (expected: 22)", (strf_data) ? size : -1);
        return NULL;
      }

      gst_buffer_map (strf_data, &info, GST_MAP_READ);
      valid_bits_per_sample = GST_READ_UINT16_LE (info.data);
      channel_mask = GST_READ_UINT32_LE (info.data + 2);
      subformat_guid[0] = GST_READ_UINT32_LE (info.data + 6);
      subformat_guid[1] = GST_READ_UINT32_LE (info.data + 10);
      subformat_guid[2] = GST_READ_UINT32_LE (info.data + 14);
      subformat_guid[3] = GST_READ_UINT32_LE (info.data + 18);
      gst_buffer_unmap (strf_data, &info);

      GST_DEBUG ("valid bps    = %u", valid_bits_per_sample);
      GST_DEBUG ("channel mask = 0x%08x", channel_mask);
      GST_DEBUG ("GUID         = %08x-%08x-%08x-%08x", subformat_guid[0],
          subformat_guid[1], subformat_guid[2], subformat_guid[3]);

      if (subformat_guid[1] == 0x00100000 &&
          subformat_guid[2] == 0xaa000080 && subformat_guid[3] == 0x719b3800) {
        if (subformat_guid[0] == 0x00000001) {
          GST_DEBUG ("PCM");
          if (strf != NULL && strf->blockalign != 0 && strf->channels != 0
              && strf->rate != 0) {
            gint ba = strf->blockalign;
            gint wd = ba * 8 / strf->channels;
            gint ws;
            GstAudioFormat format;

            /* in riff, the depth is stored in the size field but it just
             * means that the _least_ significant bits are cleared. We can
             * therefore just play the sample as if it had a depth == width */
            ws = wd;

            /* For reference, use this to get the actual depth:
             * ws = strf->bits_per_sample;
             * if (valid_bits_per_sample != 0)
             *   ws = valid_bits_per_sample; */

            format =
                gst_audio_format_build_integer (wd != 8, G_LITTLE_ENDIAN, wd,
                ws);

            caps = gst_caps_new_simple ("audio/x-raw",
                "format", G_TYPE_STRING, gst_audio_format_to_string (format),
                "layout", G_TYPE_STRING, "interleaved",
                "channels", G_TYPE_INT, strf->channels,
                "rate", G_TYPE_INT, strf->rate, NULL);

            if (codec_name) {
              *codec_name = g_strdup_printf ("Uncompressed %d-bit PCM audio",
                  strf->bits_per_sample);
            }
          }
        } else if (subformat_guid[0] == 0x00000003) {
          GST_DEBUG ("FLOAT");
          if (strf != NULL && strf->blockalign != 0 && strf->channels != 0
              && strf->rate != 0) {
            gint ba = strf->blockalign;
            gint wd = ba * 8 / strf->channels;

            caps = gst_caps_new_simple ("audio/x-raw",
                "format", G_TYPE_STRING, wd == 32 ? "F32LE" : "F64LE",
                "layout", G_TYPE_STRING, "interleaved",
                "channels", G_TYPE_INT, strf->channels,
                "rate", G_TYPE_INT, strf->rate, NULL);

            if (codec_name) {
              *codec_name =
                  g_strdup_printf ("Uncompressed %d-bit IEEE float audio",
                  strf->bits_per_sample);
            }
          }
        } else if (subformat_guid[0] == 0x0000006) {
          GST_DEBUG ("ALAW");
          if (strf != NULL) {
            if (strf->bits_per_sample != 8) {
              GST_WARNING ("invalid depth (%d) of alaw audio, overwriting.",
                  strf->bits_per_sample);
              strf->bits_per_sample = 8;
              strf->av_bps = 8;
              strf->blockalign = strf->av_bps * strf->channels;
            }
            if (strf->av_bps == 0 || strf->blockalign == 0) {
              GST_WARNING
                  ("fixing av_bps (%d) and blockalign (%d) of alaw audio",
                  strf->av_bps, strf->blockalign);
              strf->av_bps = strf->bits_per_sample;
              strf->blockalign = strf->av_bps * strf->channels;
            }
          }
          caps = gst_caps_new_empty_simple ("audio/x-alaw");

          if (codec_name)
            *codec_name = g_strdup ("A-law audio");
        } else if (subformat_guid[0] == 0x00000007) {
          GST_DEBUG ("MULAW");
          if (strf != NULL) {
            if (strf->bits_per_sample != 8) {
              GST_WARNING ("invalid depth (%d) of mulaw audio, overwriting.",
                  strf->bits_per_sample);
              strf->bits_per_sample = 8;
              strf->av_bps = 8;
              strf->blockalign = strf->av_bps * strf->channels;
            }
            if (strf->av_bps == 0 || strf->blockalign == 0) {
              GST_WARNING
                  ("fixing av_bps (%d) and blockalign (%d) of mulaw audio",
                  strf->av_bps, strf->blockalign);
              strf->av_bps = strf->bits_per_sample;
              strf->blockalign = strf->av_bps * strf->channels;
            }
          }
          caps = gst_caps_new_empty_simple ("audio/x-mulaw");
          if (codec_name)
            *codec_name = g_strdup ("Mu-law audio");
        } else if (subformat_guid[0] == 0x00000092) {
          GST_DEBUG ("FIXME: handle DOLBY AC3 SPDIF format");
          GST_DEBUG ("WAVE_FORMAT_EXTENSIBLE AC-3 SPDIF audio");
          caps = gst_caps_new_empty_simple ("audio/x-ac3");
          if (codec_name)
            *codec_name = g_strdup ("wavext AC-3 SPDIF audio");
        } else if ((subformat_guid[0] & 0xffff) ==
            GST_RIFF_WAVE_FORMAT_EXTENSIBLE) {
          GST_DEBUG ("WAVE_FORMAT_EXTENSIBLE nested");
        } else {
          /* recurse where no special consideration has yet to be identified
           * for the subformat guid */
          caps = gst_riff_create_audio_caps (subformat_guid[0], strh, strf,
              strf_data, strd_data, codec_name, channel_reorder_map);
          if (!codec_name)
            GST_DEBUG ("WAVE_FORMAT_EXTENSIBLE audio");
          if (caps) {
            if (codec_name) {
              GST_DEBUG ("WAVE_FORMAT_EXTENSIBLE %s", *codec_name);
              *codec_name = g_strjoin ("wavext ", *codec_name, NULL);
            }
            return caps;
          }
        }
      } else if (subformat_guid[0] == 0x6ba47966 &&
          subformat_guid[1] == 0x41783f83 &&
          subformat_guid[2] == 0xf0006596 && subformat_guid[3] == 0xe59262bf) {
        caps = gst_caps_new_empty_simple ("application/x-ogg-avi");
        if (codec_name)
          *codec_name = g_strdup ("Ogg-AVI");
      }

      if (caps == NULL) {
        GST_WARNING ("Unknown WAVE_FORMAT_EXTENSIBLE audio format");
        return NULL;
      }

      if (strf != NULL) {
        /* If channel_mask == 0 and channels > 1 let's
         * assume default layout as some wav files don't have the
         * channel mask set. Don't set the layout for 1 channel. */
        if (channel_mask == 0 && strf->channels > 1)
          channel_mask =
              gst_riff_wavext_get_default_channel_mask (strf->channels);

        if ((channel_mask != 0 || strf->channels > 1) &&
            !gst_riff_wavext_add_channel_mask (caps, strf->channels,
                channel_mask, channel_reorder_map)) {
          GST_WARNING ("failed to add channel layout");
          gst_caps_unref (caps);
          caps = NULL;
        }
        rate_chan = FALSE;
      }

      break;
    }
      /* can anything decode these? pitfdll? */
    case GST_RIFF_WAVE_FORMAT_VOXWARE_AC8:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_AC10:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_AC16:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_AC20:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_METAVOICE:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_METASOUND:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_RT29HW:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_VR12:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_VR18:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_TQ40:
    case GST_RIFF_WAVE_FORMAT_VOXWARE_TQ60:{
      caps = gst_caps_new_simple ("audio/x-voxware",
          "voxwaretype", G_TYPE_INT, (gint) codec_id, NULL);
      if (codec_name)
        *codec_name = g_strdup ("Voxware");
      break;
    }
    default:
    unknown:
      GST_WARNING ("Unknown audio tag 0x%04x", codec_id);
      return NULL;
  }

  if (strf != NULL) {
    if (rate_chan) {
      gst_caps_set_simple (caps,
          "rate", G_TYPE_INT, strf->rate,
          "channels", G_TYPE_INT, strf->channels, NULL);
    }
    if (block_align) {
      gst_caps_set_simple (caps,
          "block_align", G_TYPE_INT, strf->blockalign, NULL);
    }
  } else {
    if (block_align) {
      gst_caps_set_simple (caps,
          "block_align", GST_TYPE_INT_RANGE, 1, G_MAXINT, NULL);
    }
  }

  /* extradata */
  if (strf_data || strd_data) {
    gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER,
        strf_data ? strf_data : strd_data, NULL);
  }

  return caps;
}


libvncclient: free vncRec memory in rfbClientCleanup()

Otherwise we leak memory. Spotted by Ramin Farajpour Cami
<ramin.blackhat@gmail.com>, thanks!

void rfbClientCleanup(rfbClient* client) {
#ifdef LIBVNCSERVER_HAVE_LIBZ
  int i;

  for ( i = 0; i < 4; i++ ) {
    if (client->zlibStreamActive[i] == TRUE ) {
      if (inflateEnd (&client->zlibStream[i]) != Z_OK &&
	  client->zlibStream[i].msg != NULL)
	rfbClientLog("inflateEnd: %s\n", client->zlibStream[i].msg);
    }
  }

  if ( client->decompStreamInited == TRUE ) {
    if (inflateEnd (&client->decompStream) != Z_OK &&
	client->decompStream.msg != NULL)
      rfbClientLog("inflateEnd: %s\n", client->decompStream.msg );
  }
#endif

  if (client->ultra_buffer)
    free(client->ultra_buffer);

  if (client->raw_buffer)
    free(client->raw_buffer);

  FreeTLS(client);

  while (client->clientData) {
    rfbClientData* next = client->clientData->next;
    free(client->clientData);
    client->clientData = next;
  }

  if (client->sock != RFB_INVALID_SOCKET)
    rfbCloseSocket(client->sock);
  if (client->listenSock != RFB_INVALID_SOCKET)
    rfbCloseSocket(client->listenSock);
  free(client->desktopName);
  free(client->serverHost);
  if (client->destHost)
    free(client->destHost);
  if (client->clientAuthSchemes)
    free(client->clientAuthSchemes);

#ifdef LIBVNCSERVER_HAVE_SASL
  if (client->saslSecret)
    free(client->saslSecret);
#endif /* LIBVNCSERVER_HAVE_SASL */

#ifdef WIN32
  if(WSACleanup() != 0) {
      errno=WSAGetLastError();
      rfbClientErr("Could not terminate Windows Sockets: %s\n", strerror(errno));
  }
#endif

  free(client);
}

libvncclient v0.9.13 was discovered to contain a memory leak via the function rfbClientCleanup().


void rfbClientCleanup(rfbClient* client) {
#ifdef LIBVNCSERVER_HAVE_LIBZ
  int i;

  for ( i = 0; i < 4; i++ ) {
    if (client->zlibStreamActive[i] == TRUE ) {
      if (inflateEnd (&client->zlibStream[i]) != Z_OK &&
	  client->zlibStream[i].msg != NULL)
	rfbClientLog("inflateEnd: %s\n", client->zlibStream[i].msg);
    }
  }

  if ( client->decompStreamInited == TRUE ) {
    if (inflateEnd (&client->decompStream) != Z_OK &&
	client->decompStream.msg != NULL)
      rfbClientLog("inflateEnd: %s\n", client->decompStream.msg );
  }
#endif

  if (client->ultra_buffer)
    free(client->ultra_buffer);

  if (client->raw_buffer)
    free(client->raw_buffer);

  FreeTLS(client);

  while (client->clientData) {
    rfbClientData* next = client->clientData->next;
    free(client->clientData);
    client->clientData = next;
  }

  free(client->vncRec);

  if (client->sock != RFB_INVALID_SOCKET)
    rfbCloseSocket(client->sock);
  if (client->listenSock != RFB_INVALID_SOCKET)
    rfbCloseSocket(client->listenSock);
  free(client->desktopName);
  free(client->serverHost);
  if (client->destHost)
    free(client->destHost);
  if (client->clientAuthSchemes)
    free(client->clientAuthSchemes);

#ifdef LIBVNCSERVER_HAVE_SASL
  if (client->saslSecret)
    free(client->saslSecret);
#endif /* LIBVNCSERVER_HAVE_SASL */

#ifdef WIN32
  if(WSACleanup() != 0) {
      errno=WSAGetLastError();
      rfbClientErr("Could not terminate Windows Sockets: %s\n", strerror(errno));
  }
#endif

  free(client);
}


dccp: Fix memleak in __feat_register_sp

If dccp_feat_push_change fails, we forget free the mem
which is alloced by kmemdup in dccp_feat_clone_sp_val.

Reported-by: Hulk Robot <hulkci@huawei.com>
Fixes: e8ef967a54f4 ("dccp: Registration routines for changing feature values")
Reviewed-by: Mukesh Ojha <mojha@codeaurora.org>
Signed-off-by: YueHaibing <yuehaibing@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local,
			      u8 mandatory, u8 const *sp_val, u8 sp_len)
{
	dccp_feat_val fval;

	if (dccp_feat_type(feat) != FEAT_SP ||
	    !dccp_feat_sp_list_ok(feat, sp_val, sp_len))
		return -EINVAL;

	/* Avoid negotiating alien CCIDs by only advertising supported ones */
	if (feat == DCCPF_CCID && !ccid_support_check(sp_val, sp_len))
		return -EOPNOTSUPP;

	if (dccp_feat_clone_sp_val(&fval, sp_val, sp_len))
		return -ENOMEM;

	return dccp_feat_push_change(fn, feat, is_local, mandatory, &fval);
}

In the Linux kernel before 5.1, there is a memory leak in __feat_register_sp() in net/dccp/feat.c, which may cause denial of service, aka CID-1d3ff0950e2b.


static int __feat_register_sp(struct list_head *fn, u8 feat, u8 is_local,
			      u8 mandatory, u8 const *sp_val, u8 sp_len)
{
	dccp_feat_val fval;

	if (dccp_feat_type(feat) != FEAT_SP ||
	    !dccp_feat_sp_list_ok(feat, sp_val, sp_len))
		return -EINVAL;

	/* Avoid negotiating alien CCIDs by only advertising supported ones */
	if (feat == DCCPF_CCID && !ccid_support_check(sp_val, sp_len))
		return -EOPNOTSUPP;

	if (dccp_feat_clone_sp_val(&fval, sp_val, sp_len))
		return -ENOMEM;

	if (dccp_feat_push_change(fn, feat, is_local, mandatory, &fval)) {
		kfree(fval.sp.vec);
		return -ENOMEM;
	}

	return 0;
}


static Image *ReadSUNImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
#define RMT_EQUAL_RGB  1
#define RMT_NONE  0
#define RMT_RAW  2
#define RT_STANDARD  1
#define RT_ENCODED  2
#define RT_FORMAT_RGB  3

  typedef struct _SUNInfo
  {
    unsigned int
      magic,
      width,
      height,
      depth,
      length,
      type,
      maptype,
      maplength;
  } SUNInfo;

  Image
    *image;

  int
    bit;

  MagickBooleanType
    status;

  MagickSizeType
    number_pixels;

  register Quantum
    *q;

  register ssize_t
    i,
    x;

  register unsigned char
    *p;

  size_t
    bytes_per_line,
    extent,
    height,
    pixels_length,
    quantum;

  ssize_t
    count,
    y;

  SUNInfo
    sun_info;

  unsigned char
    *sun_data,
    *sun_pixels;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickCoreSignature);
  image=AcquireImage(image_info,exception);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
    Read SUN raster header.
  */
  (void) ResetMagickMemory(&sun_info,0,sizeof(sun_info));
  sun_info.magic=ReadBlobMSBLong(image);
  do
  {
    /*
      Verify SUN identifier.
    */
    if (sun_info.magic != 0x59a66a95)
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    sun_info.width=ReadBlobMSBLong(image);
    sun_info.height=ReadBlobMSBLong(image);
    sun_info.depth=ReadBlobMSBLong(image);
    sun_info.length=ReadBlobMSBLong(image);
    sun_info.type=ReadBlobMSBLong(image);
    sun_info.maptype=ReadBlobMSBLong(image);
    sun_info.maplength=ReadBlobMSBLong(image);
    if (sun_info.maplength > GetBlobSize(image))
      ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
    extent=sun_info.height*sun_info.width;
    if ((sun_info.height != 0) && (sun_info.width != extent/sun_info.height))
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    if ((sun_info.type != RT_STANDARD) && (sun_info.type != RT_ENCODED) &&
        (sun_info.type != RT_FORMAT_RGB))
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    if ((sun_info.maptype == RMT_NONE) && (sun_info.maplength != 0))
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    if ((sun_info.depth != 1) && (sun_info.depth != 8) &&
        (sun_info.depth != 24) && (sun_info.depth != 32))
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    if ((sun_info.maptype != RMT_NONE) && (sun_info.maptype != RMT_EQUAL_RGB) &&
        (sun_info.maptype != RMT_RAW))
      ThrowReaderException(CoderError,"ColormapTypeNotSupported");
    image->columns=sun_info.width;
    image->rows=sun_info.height;
    image->depth=sun_info.depth <= 8 ? sun_info.depth :
      MAGICKCORE_QUANTUM_DEPTH;
    if (sun_info.depth < 24)
      {
        size_t
          one;

        image->colors=sun_info.maplength;
        one=1;
        if (sun_info.maptype == RMT_NONE)
          image->colors=one << sun_info.depth;
        if (sun_info.maptype == RMT_EQUAL_RGB)
          image->colors=sun_info.maplength/3;
        if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      }
    switch (sun_info.maptype)
    {
      case RMT_NONE:
        break;
      case RMT_EQUAL_RGB:
      {
        unsigned char
          *sun_colormap;

        /*
          Read SUN raster colormap.
        */
        sun_colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
          sizeof(*sun_colormap));
        if (sun_colormap == (unsigned char *) NULL)
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        count=ReadBlob(image,image->colors,sun_colormap);
        if (count != (ssize_t) image->colors)
          {
            sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
            ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
          }
        for (i=0; i < (ssize_t) image->colors; i++)
          image->colormap[i].red=(MagickRealType) ScaleCharToQuantum(
            sun_colormap[i]);
        count=ReadBlob(image,image->colors,sun_colormap);
        if (count != (ssize_t) image->colors)
          {
            sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
            ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
          }
        for (i=0; i < (ssize_t) image->colors; i++)
          image->colormap[i].green=(MagickRealType) ScaleCharToQuantum(
            sun_colormap[i]);
        count=ReadBlob(image,image->colors,sun_colormap);
        if (count != (ssize_t) image->colors)
          {
            sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
            ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
          }
        for (i=0; i < (ssize_t) image->colors; i++)
          image->colormap[i].blue=(MagickRealType) ScaleCharToQuantum(
            sun_colormap[i]);
        sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
        break;
      }
      case RMT_RAW:
      {
        unsigned char
          *sun_colormap;

        /*
          Read SUN raster colormap.
        */
        sun_colormap=(unsigned char *) AcquireQuantumMemory(sun_info.maplength,
          sizeof(*sun_colormap));
        if (sun_colormap == (unsigned char *) NULL)
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        count=ReadBlob(image,sun_info.maplength,sun_colormap);
        sun_colormap=(unsigned char *) RelinquishMagickMemory(sun_colormap);
        if (count != (ssize_t) sun_info.maplength)
          ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
        break;
      }
      default:
        ThrowReaderException(CoderError,"ColormapTypeNotSupported");
    }
    image->alpha_trait=sun_info.depth == 32 ? BlendPixelTrait :
      UndefinedPixelTrait;
    image->columns=sun_info.width;
    image->rows=sun_info.height;
    if (image_info->ping != MagickFalse)
      {
        (void) CloseBlob(image);
        return(GetFirstImageInList(image));
      }
    status=SetImageExtent(image,image->columns,image->rows,exception);
    if (status == MagickFalse)
      return(DestroyImageList(image));
    if (sun_info.length == 0)
      ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
    number_pixels=(MagickSizeType) (image->columns*image->rows);
    if ((sun_info.type != RT_ENCODED) &&
        ((number_pixels*sun_info.depth) > (8UL*sun_info.length)))
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    if (HeapOverflowSanityCheck(sun_info.width,sun_info.depth) != MagickFalse)
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    bytes_per_line=sun_info.width*sun_info.depth;
    if (sun_info.length > GetBlobSize(image))
      ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
    sun_data=(unsigned char *) AcquireQuantumMemory(sun_info.length,
      sizeof(*sun_data));
    if (sun_data == (unsigned char *) NULL)
      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    count=(ssize_t) ReadBlob(image,sun_info.length,sun_data);
    if (count != (ssize_t) sun_info.length)
      {
        sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
        ThrowReaderException(CorruptImageError,"UnableToReadImageData");
      }
    height=sun_info.height;
    if ((height == 0) || (sun_info.width == 0) || (sun_info.depth == 0) ||
        ((bytes_per_line/sun_info.depth) != sun_info.width))
      {
        sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
        ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
      }
    quantum=sun_info.depth == 1 ? 15 : 7;
    bytes_per_line+=quantum;
    bytes_per_line<<=1;
    if ((bytes_per_line >> 1) != (sun_info.width*sun_info.depth+quantum))
      {
        sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
        ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
      }
    bytes_per_line>>=4;
    if (HeapOverflowSanityCheck(height,bytes_per_line) != MagickFalse)
      {
        sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
        ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
      }
    pixels_length=height*bytes_per_line;
    sun_pixels=(unsigned char *) AcquireQuantumMemory(pixels_length+image->rows,
      sizeof(*sun_pixels));
    if (sun_pixels == (unsigned char *) NULL)
      {
        sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      }
    ResetMagickMemory(sun_pixels,0,pixels_length*sizeof(*sun_pixels));
    if (sun_info.type == RT_ENCODED)
      {
        status=DecodeImage(sun_data,sun_info.length,sun_pixels,pixels_length);
        if (status == MagickFalse)
          {
            sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
            sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
            ThrowReaderException(CorruptImageError,"UnableToReadImageData");
          }
      }
    else
      {
        if (sun_info.length > pixels_length)
          {
            sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
            sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
            ThrowReaderException(ResourceLimitError,"ImproperImageHeader");
          }
        (void) CopyMagickMemory(sun_pixels,sun_data,sun_info.length);
      }
    sun_data=(unsigned char *) RelinquishMagickMemory(sun_data);
    /*
      Convert SUN raster image to pixel packets.
    */
    p=sun_pixels;
    if (sun_info.depth == 1)
      for (y=0; y < (ssize_t) image->rows; y++)
      {
        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
        if (q == (Quantum *) NULL)
          break;
        for (x=0; x < ((ssize_t) image->columns-7); x+=8)
        {
          for (bit=7; bit >= 0; bit--)
          {
            SetPixelIndex(image,(Quantum) ((*p) & (0x01 << bit) ? 0x00 : 0x01),
              q);
            q+=GetPixelChannels(image);
          }
          p++;
        }
        if ((image->columns % 8) != 0)
          {
            for (bit=7; bit >= (int) (8-(image->columns % 8)); bit--)
            {
              SetPixelIndex(image,(Quantum) ((*p) & (0x01 << bit) ? 0x00 :
                0x01),q);
              q+=GetPixelChannels(image);
            }
            p++;
          }
        if ((((image->columns/8)+(image->columns % 8 ? 1 : 0)) % 2) != 0)
          p++;
        if (SyncAuthenticPixels(image,exception) == MagickFalse)
          break;
        if (image->previous == (Image *) NULL)
          {
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
              image->rows);
            if (status == MagickFalse)
              break;
          }
      }
    else
      if (image->storage_class == PseudoClass)
        {
          for (y=0; y < (ssize_t) image->rows; y++)
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (Quantum *) NULL)
              break;
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              SetPixelIndex(image,ConstrainColormapIndex(image,*p,exception),q);
              p++;
              q+=GetPixelChannels(image);
            }
            if ((image->columns % 2) != 0)
              p++;
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            if (image->previous == (Image *) NULL)
              {
                status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
                image->rows);
                if (status == MagickFalse)
                  break;
              }
          }
        }
      else
        {
          size_t
            bytes_per_pixel;

          bytes_per_pixel=3;
          if (image->alpha_trait != UndefinedPixelTrait)
            bytes_per_pixel++;
          if (bytes_per_line == 0)
            bytes_per_line=bytes_per_pixel*image->columns;
          for (y=0; y < (ssize_t) image->rows; y++)
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (Quantum *) NULL)
              break;
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              if (image->alpha_trait != UndefinedPixelTrait)
                SetPixelAlpha(image,ScaleCharToQuantum(*p++),q);
              if (sun_info.type == RT_STANDARD)
                {
                  SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
                  SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
                  SetPixelRed(image,ScaleCharToQuantum(*p++),q);
                }
              else
                {
                  SetPixelRed(image,ScaleCharToQuantum(*p++),q);
                  SetPixelGreen(image,ScaleCharToQuantum(*p++),q);
                  SetPixelBlue(image,ScaleCharToQuantum(*p++),q);
                }
              if (image->colors != 0)
                {
                  SetPixelRed(image,ClampToQuantum(image->colormap[(ssize_t)
                    GetPixelRed(image,q)].red),q);
                  SetPixelGreen(image,ClampToQuantum(image->colormap[(ssize_t)
                    GetPixelGreen(image,q)].green),q);
                  SetPixelBlue(image,ClampToQuantum(image->colormap[(ssize_t)
                    GetPixelBlue(image,q)].blue),q);
                }
              q+=GetPixelChannels(image);
            }
            if (((bytes_per_pixel*image->columns) % 2) != 0)
              p++;
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            if (image->previous == (Image *) NULL)
              {
                status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
                image->rows);
                if (status == MagickFalse)
                  break;
              }
          }
        }
    if (image->storage_class == PseudoClass)
      (void) SyncImage(image,exception);
    sun_pixels=(unsigned char *) RelinquishMagickMemory(sun_pixels);
    if (EOFBlob(image) != MagickFalse)
      {
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
          image->filename);
        break;
      }
    /*
      Proceed to next image.
    */
    if (image_info->number_scenes != 0)
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    sun_info.magic=ReadBlobMSBLong(image);
    if (sun_info.magic == 0x59a66a95)
      {
        /*
          Allocate next image structure.
        */
        AcquireNextImage(image_info,image,exception);
        if (GetNextImageInList(image) == (Image *) NULL)
          {
            image=DestroyImageList(image);
            return((Image *) NULL);
          }
        image=SyncNextImageInList(image);
        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
          GetBlobSize(image));
        if (status == MagickFalse)
          break;
      }
  } while (sun_info.magic == 0x59a66a95);
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}

In ImageMagick 7.0.7-23 Q16 x86_64 2018-01-24, there is a heap-based buffer over-read in ReadSUNImage in coders/sun.c, which allows attackers to cause a denial of service (application crash in SetGrayscaleImage in MagickCore/quantize.c) via a crafted SUN image file.