CVE-2017-7586 (C, published 2017-04-07T20:59Z)
Description: value: In libsndfile before 1.0.28, an error in the "header_read()" function (common.c) when handling ID3 tags can be exploited to cause a stack-based buffer overflow via a specially crafted FLAC file.
Severity: MEDIUM (CVSS v3 base score: 5.5)
Weakness: CWE-119 \u2014 Improper Restriction of Operations within the Bounds of a Memory Buffer
The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
Repository: https://github.com/erikd/libsndfile
Files changed:
src/common.c
src/common.h
src/sndfile.c
Commit message: src/ : Move to a variable length header buffer

Previously, the `psf->header` buffer was a fixed length specified by
`SF_HEADER_LEN` which was set to `12292`. This was problematic for
two reasons; this value was un-necessarily large for the majority
of files and too small for some others.

Now the size of the header buffer starts at 256 bytes and grows as
necessary up to a maximum of 100k.


Vulnerable code:
	maxlen = strlen ((char*) psf->header) ;
	start	= ((char*) psf->header) + maxlen ;
	maxlen	= sizeof (psf->header) - maxlen ;
	psf->headindex = strlen ((char*) psf->header) ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 1)
		psf->header [psf->headindex++] = x ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 4)
	{	psf->header [psf->headindex++] = (x >> 24) ;
		psf->header [psf->headindex++] = (x >> 16) ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = x ;
		} ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 4)
	{	psf->header [psf->headindex++] = x ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = (x >> 16) ;
		psf->header [psf->headindex++] = (x >> 24) ;
		} ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 2)
	{	psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = x ;
		} ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 2)
	{	psf->header [psf->headindex++] = x ;
		psf->header [psf->headindex++] = (x >> 8) ;
		} ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 3)
	{	psf->header [psf->headindex++] = (x >> 16) ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = x ;
		} ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 3)
	{	psf->header [psf->headindex++] = x ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = (x >> 16) ;
		} ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 4)
	{	psf->header [psf->headindex++] = (x >> 24) ;
		psf->header [psf->headindex++] = (x >> 16) ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = x ;
		} ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 4)
	{	psf->header [psf->headindex++] = x ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = (x >> 16) ;
		psf->header [psf->headindex++] = (x >> 24) ;
		} ;
#if (SIZEOF_SF_COUNT_T == 4)

static inline void
header_put_be_8byte (SF_PRIVATE *psf, sf_count_t x)
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 8)
	{	psf->header [psf->headindex++] = 0 ;
		psf->header [psf->headindex++] = 0 ;
		psf->header [psf->headindex++] = 0 ;
		psf->header [psf->headindex++] = 0 ;
		psf->header [psf->headindex++] = (x >> 24) ;
		psf->header [psf->headindex++] = (x >> 16) ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = x ;
		} ;
} /* header_put_be_8byte */

static inline void
header_put_le_8byte (SF_PRIVATE *psf, sf_count_t x)
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 8)
	{	psf->header [psf->headindex++] = x ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = (x >> 16) ;
		psf->header [psf->headindex++] = (x >> 24) ;
		psf->header [psf->headindex++] = 0 ;
		psf->header [psf->headindex++] = 0 ;
		psf->header [psf->headindex++] = 0 ;
		psf->header [psf->headindex++] = 0 ;
		} ;
} /* header_put_le_8byte */

#elif (SIZEOF_SF_COUNT_T == 8)
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 8)
	{	psf->header [psf->headindex++] = (x >> 56) ;
		psf->header [psf->headindex++] = (x >> 48) ;
		psf->header [psf->headindex++] = (x >> 40) ;
		psf->header [psf->headindex++] = (x >> 32) ;
		psf->header [psf->headindex++] = (x >> 24) ;
		psf->header [psf->headindex++] = (x >> 16) ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = x ;
		} ;
{	if (psf->headindex < SIGNED_SIZEOF (psf->header) - 8)
	{	psf->header [psf->headindex++] = x ;
		psf->header [psf->headindex++] = (x >> 8) ;
		psf->header [psf->headindex++] = (x >> 16) ;
		psf->header [psf->headindex++] = (x >> 24) ;
		psf->header [psf->headindex++] = (x >> 32) ;
		psf->header [psf->headindex++] = (x >> 40) ;
		psf->header [psf->headindex++] = (x >> 48) ;
		psf->header [psf->headindex++] = (x >> 56) ;
		} ;
#error "SIZEOF_SF_COUNT_T is not defined."
	{	switch (c)
						float32_be_write (floatdata, psf->header + psf->headindex) ;
						float32_le_write (floatdata, psf->header + psf->headindex) ;
					psf->headindex += 4 ;
						double64_be_write (doubledata, psf->header + psf->headindex) ;
						double64_le_write (doubledata, psf->header + psf->headindex) ;
					psf->headindex += 8 ;
					memcpy (&(psf->header [psf->headindex]), strptr, size) ;
					psf->headindex += size ;
					psf->header [psf->headindex - 1] = 0 ;
					memcpy (&(psf->header [psf->headindex]), strptr, size + 1) ;
					psf->headindex += size ;
					psf->header [psf->headindex] = 0 ;
					memcpy (&(psf->header [psf->headindex]), strptr, size) ;
					psf->headindex += size ;
					if (psf->headindex + size < sizeof (psf->header))
					{	memcpy (&(psf->header [psf->headindex]), bindata, size) ;
						psf->headindex += size ;
						count += size ;
						} ;
					{	psf->header [psf->headindex] = 0 ;
						psf->headindex ++ ;
					memcpy (&(psf->header [psf->headindex]), bindata, 16) ;
					psf->headindex += 16 ;
					psf->headindex += size ;
					if (size < sizeof (psf->header))
					{	psf->headindex = size ;
						count = 0 ;
						} ;
	if (psf->headindex >= SIGNED_SIZEOF (psf->header))
		return psf_fread (ptr, 1, bytes, psf) ;

	if (psf->headindex + bytes > SIGNED_SIZEOF (psf->header))
	{	int most ;
		most = SIGNED_SIZEOF (psf->header) - psf->headend ;
		psf_fread (psf->header + psf->headend, 1, most, psf) ;
		memcpy (ptr, psf->header + psf->headend, most) ;
		psf->headend = psf->headindex += most ;
		psf_fread ((char *) ptr + most, bytes - most, 1, psf) ;
		return bytes ;
		} ;

	if (psf->headindex + bytes > psf->headend)
	{	count = psf_fread (psf->header + psf->headend, 1, bytes - (psf->headend - psf->headindex), psf) ;
		if (count != bytes - (int) (psf->headend - psf->headindex))
		psf->headend += count ;
	memcpy (ptr, psf->header + psf->headindex, bytes) ;
	psf->headindex += bytes ;
			if (position > SIGNED_SIZEOF (psf->header))
			if (position > psf->headend)
				psf->headend += psf_fread (psf->header + psf->headend, 1, position - psf->headend, psf) ;
			psf->headindex = position ;
			if (psf->headindex + position < 0)
			if (psf->headindex >= SIGNED_SIZEOF (psf->header))
			if (psf->headindex + position <= psf->headend)
			{	psf->headindex += position ;
			if (psf->headindex + position > SIGNED_SIZEOF (psf->header))
				psf->headindex = psf->headend ;
			psf->headend += psf_fread (psf->header + psf->headend, 1, position - (psf->headend - psf->headindex), psf) ;
			psf->headindex = psf->headend ;
	{	if (psf->headindex < psf->headend)
		{	ptr [k] = psf->header [psf->headindex] ;
			psf->headindex ++ ;
		{	psf->headend += psf_fread (psf->header + psf->headend, 1, 1, psf) ;
			ptr [k] = psf->header [psf->headindex] ;
			psf->headindex = psf->headend ;
	int				byte_count = 0, count ;
	{	switch (c)
					memcpy (&(psf->header [psf->headindex]), strptr, size) ;
					psf->headindex += size ;
					if (count > 0)
						byte_count += header_read (psf, charptr, count) ;
					if (count > 0)
						byte_count += header_gets (psf, charptr, count) ;
					{	psf->header [psf->headindex] = 0 ;
						psf->headindex ++ ;
			case 'p' :
					/* Get the seek position first. */
			case 'j' :
					/* Get the seek position first. */
					if (count)
					{	header_seek (psf, count, SEEK_CUR) ;
						byte_count += count ;
						} ;
#define	SF_HEADER_LEN			(12292)
	unsigned char	header		[SF_HEADER_LEN] ; /* Must be unsigned */
	/* Index variables for maintaining parselog and header above. */
	int				headindex, headend ;
	int				has_text ;


	if ((psf = calloc (1, sizeof (SF_PRIVATE))) == NULL)
	if ((psf = calloc (1, sizeof (SF_PRIVATE))) == NULL)
	if ((psf = calloc (1, sizeof (SF_PRIVATE))) == NULL)

Fixed code:
#define	INITAL_HEADER_SIZE	256

/* Allocate and initialize the SF_PRIVATE struct. */
SF_PRIVATE *
psf_allocate (void)
{	SF_PRIVATE * psf ;

	if ((psf = calloc (1, sizeof (SF_PRIVATE))) == NULL)
		return	NULL ;

	if ((psf->header.ptr = calloc (1, INITAL_HEADER_SIZE)) == NULL)
	{	free (psf) ;
		return	NULL ;
		} ;
	psf->header.len = INITAL_HEADER_SIZE ;

	return psf ;
} /* psf_allocate */

static int
psf_bump_header_allocation (SF_PRIVATE * psf, sf_count_t needed)
{
	sf_count_t newlen, smallest = INITAL_HEADER_SIZE ;
	void * ptr ;

	newlen = (needed > psf->header.len) ? 2 * SF_MAX (needed, smallest) : 2 * psf->header.len ;

	if (newlen > 100 * 1024)
	{	psf_log_printf (psf, "Request for header allocation of %D denined.\n", newlen) ;
		return 1 ;
		}

	if ((ptr = realloc (psf->header.ptr, newlen)) == NULL)
	{	psf_log_printf (psf, "realloc (%p, %D) failed\n", psf->header.ptr, newlen) ;
		psf->error = SFE_MALLOC_FAILED ;
		return 1 ;
		} ;

	psf->header.ptr = ptr ;
	psf->header.len = newlen ;
	return 0 ;
} /* psf_bump_header_allocation */

	maxlen = strlen ((char*) psf->header.ptr) ;
	start	= ((char*) psf->header.ptr) + maxlen ;
	maxlen	= psf->header.len - maxlen ;
	psf->header.indx = strlen ((char*) psf->header.ptr) ;
{	psf->header.ptr [psf->header.indx++] = x ;
{	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
	psf->header.ptr [psf->header.indx++] = x ;
{	psf->header.ptr [psf->header.indx++] = x ;
	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
{	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
	psf->header.ptr [psf->header.indx++] = x ;
{	psf->header.ptr [psf->header.indx++] = x ;
	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
{	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
	psf->header.ptr [psf->header.indx++] = x ;
{	psf->header.ptr [psf->header.indx++] = x ;
	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
{	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
	psf->header.ptr [psf->header.indx++] = x ;
{	psf->header.ptr [psf->header.indx++] = x ;
	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
#if (SIZEOF_SF_COUNT_T == 8)
{	psf->header.ptr [psf->header.indx++] = (x >> 56) ;
	psf->header.ptr [psf->header.indx++] = (x >> 48) ;
	psf->header.ptr [psf->header.indx++] = (x >> 40) ;
	psf->header.ptr [psf->header.indx++] = (x >> 32) ;
	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
	psf->header.ptr [psf->header.indx++] = x ;
{	psf->header.ptr [psf->header.indx++] = x ;
	psf->header.ptr [psf->header.indx++] = (x >> 8) ;
	psf->header.ptr [psf->header.indx++] = (x >> 16) ;
	psf->header.ptr [psf->header.indx++] = (x >> 24) ;
	psf->header.ptr [psf->header.indx++] = (x >> 32) ;
	psf->header.ptr [psf->header.indx++] = (x >> 40) ;
	psf->header.ptr [psf->header.indx++] = (x >> 48) ;
	psf->header.ptr [psf->header.indx++] = (x >> 56) ;
#error "SIZEOF_SF_COUNT_T != 8"
	{
		if (psf->header.indx + 16 >= psf->header.len && psf_bump_header_allocation (psf, 16))
			return count ;

		switch (c)
						float32_be_write (floatdata, psf->header.ptr + psf->header.indx) ;
						float32_le_write (floatdata, psf->header.ptr + psf->header.indx) ;
					psf->header.indx += 4 ;
						double64_be_write (doubledata, psf->header.ptr + psf->header.indx) ;
						double64_le_write (doubledata, psf->header.ptr + psf->header.indx) ;
					psf->header.indx += 8 ;

					if (psf->header.indx + (sf_count_t) size >= psf->header.len && psf_bump_header_allocation (psf, 16))
						return count ;

					memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size) ;
					psf->header.indx += size ;
					psf->header.ptr [psf->header.indx - 1] = 0 ;
					if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size))
						return count ;
					memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size + 1) ;
					psf->header.indx += size ;
					psf->header.ptr [psf->header.indx] = 0 ;

					if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size))
						return count ;

					memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size) ;
					psf->header.indx += size ;

					if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size))
						return count ;

					memcpy (&(psf->header.ptr [psf->header.indx]), bindata, size) ;
					psf->header.indx += size ;
					count += size ;

					if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size))
						return count ;

					{	psf->header.ptr [psf->header.indx] = 0 ;
						psf->header.indx ++ ;
					memcpy (&(psf->header.ptr [psf->header.indx]), bindata, 16) ;
					psf->header.indx += 16 ;

					if (psf->header.indx + (sf_count_t) size > psf->header.len && psf_bump_header_allocation (psf, size))
						return count ;

					psf->header.indx += size ;

					if ((sf_count_t) size >= psf->header.len && psf_bump_header_allocation (psf, size))
						return count ;

					psf->header.indx = size ;
	if (psf->header.indx + bytes >= psf->header.len && psf_bump_header_allocation (psf, bytes))
		return count ;
	if (psf->header.indx + bytes > psf->header.end)
	{	count = psf_fread (psf->header.ptr + psf->header.end, 1, bytes - (psf->header.end - psf->header.indx), psf) ;
		if (count != bytes - (int) (psf->header.end - psf->header.indx))
		psf->header.end += count ;
	memcpy (ptr, psf->header.ptr + psf->header.indx, bytes) ;
	psf->header.indx += bytes ;
			if (psf->header.indx + position >= psf->header.len)
				psf_bump_header_allocation (psf, position) ;
			if (position > psf->header.len)
			if (position > psf->header.end)
				psf->header.end += psf_fread (psf->header.ptr + psf->header.end, 1, position - psf->header.end, psf) ;
			psf->header.indx = position ;
			if (psf->header.indx + position >= psf->header.len)
				psf_bump_header_allocation (psf, position) ;

			if (psf->header.indx + position < 0)
			if (psf->header.indx >= psf->header.len)
			if (psf->header.indx + position <= psf->header.end)
			{	psf->header.indx += position ;
			if (psf->header.indx + position > psf->header.len)
				psf->header.indx = psf->header.end ;
			psf->header.end += psf_fread (psf->header.ptr + psf->header.end, 1, position - (psf->header.end - psf->header.indx), psf) ;
			psf->header.indx = psf->header.end ;
	if (psf->header.indx + bufsize >= psf->header.len && psf_bump_header_allocation (psf, bufsize))
		return 0 ;

	{	if (psf->header.indx < psf->header.end)
		{	ptr [k] = psf->header.ptr [psf->header.indx] ;
			psf->header.indx ++ ;
		{	psf->header.end += psf_fread (psf->header.ptr + psf->header.end, 1, 1, psf) ;
			ptr [k] = psf->header.ptr [psf->header.indx] ;
			psf->header.indx = psf->header.end ;
	int				byte_count = 0, count = 0 ;
	{
		if (psf->header.indx + 16 >= psf->header.len && psf_bump_header_allocation (psf, 16))
			return count ;

		switch (c)
					*intptr = 0 ;
					*intptr = 0 ;
					memcpy (&(psf->header.ptr [psf->header.indx]), strptr, size) ;
					psf->header.indx += size ;
					memset (charptr, 0, count) ;
					byte_count += header_read (psf, charptr, count) ;
					memset (charptr, 0, count) ;

					if (psf->header.indx + count >= psf->header.len && psf_bump_header_allocation (psf, count))
						return 0 ;

					byte_count += header_gets (psf, charptr, count) ;
					{	psf->header.ptr [psf->header.indx] = 0 ;
						psf->header.indx ++ ;
			case 'p' :	/* Seek to position from start. */
			case 'j' :	/* Seek to position from current position. */
					header_seek (psf, count, SEEK_CUR) ;
					byte_count += count ;

	struct
	{	unsigned char	* ptr ;
		sf_count_t		indx, end, len ;
	} header ;

/* Allocate and initialize the SF_PRIVATE struct. */
SF_PRIVATE * psf_allocate (void) ;

	{	SFE_BAD_HEADER_ALLOC 	, "Error : Required header allocation is too large." },
	if ((psf = psf_allocate ()) == NULL)
	if ((psf = psf_allocate ()) == NULL)
	if ((psf = psf_allocate ()) == NULL)
	free (psf->header.ptr) ;