Commit message: Fix left shift of a negative value in SWFInput_readSBits. Check for number before before left-shifting by (number-1).
CVE: CVE-2019-12980
CWE: CWE-703
Vulnerability description: In Ming (aka libming) 0.4.8, there is an integer overflow (caused by an out-of-range left shift) in the SWFInput_readSBits function in blocks/input.c. Remote attackers could leverage this vulnerability to cause a denial-of-service via a crafted swf file.

Project: libming

SWFInput_seek(SWFInput input, long offset, int whence)
{
	input->seek(input, offset, whence);
}

Project: libming

SWFInput_stream_seek(SWFInput input, long offset, int whence)
{
	int len, l, readOffset;
	struct SWFInputStreamData *data;

	if ( whence == SEEK_CUR )
		input->offset = input->offset + offset;

	else if ( whence == SEEK_SET )
		input->offset = offset;

	else if ( whence == SEEK_END )
	{
		/* suck data until EOF */
		/* XXX - might want to put a limit on how much we suck (ha) */

		while ( SWFInput_getChar(input) != EOF )
			if (input->length > MAX_INPUTSTREAM)
				break;

		input->offset = input->length - offset;
	}

	if (input->offset < 0)
		input->offset = 0;

	if ( input->offset < input->length )
		return;

	/* now slurp up as much data as we need to get here */

	len = (((input->offset - input->length)/INPUTSTREAM_INCREMENT)+1) * INPUTSTREAM_INCREMENT;

	readOffset = input->length;
	input->length += len;
	data = (struct SWFInputStreamData*) input->data;

	data->buffer = (unsigned char*)realloc(data->buffer, sizeof(unsigned char) * (input->length + len));

	l=1; /* just to initialize to something */
	while((len > 0) && (l > 0))
	{
		l = fread(data->buffer, sizeof(unsigned char), len, data->file);
		len -= l;
		readOffset += l;
	}
}

Project: libming

SWFInput_stream_getChar(SWFInput input)
{
	struct SWFInputStreamData *data = (struct SWFInputStreamData *) input->data;

	if (input->offset >= MAX_INPUTSTREAM)
		return EOF;

	if ( input->offset == input->length )
	{
		/* fetch from stream, save in buffer */

		FILE *f = data->file;
		int c = fgetc(f);

		++input->offset;

		if ( c != EOF )
		{
			if ( input->length % INPUTSTREAM_INCREMENT == 0 )
			{
				data->buffer = (unsigned char*) realloc(data->buffer,
															 sizeof(unsigned char) *
															 (input->length + INPUTSTREAM_INCREMENT));
			}

			data->buffer[input->length] = c;
			++input->length;
		}

		return c;
	}
	else if ( input->offset < input->length )
	{
		/* fetch from buffer */
		return data->buffer[input->offset++];
	}
	else
		return EOF;
}

Project: libming

SWFInput_byteAlign(SWFInput input)
{
	if(input->bufbits > 0)
	{
		input->bufbits = 0;
		input->buffer = 0;
	}
}

Project: libming

SWFInput_getUInt32_BE(SWFInput input)
{
	unsigned long num = SWFInput_getChar(input) << 24;
	num += SWFInput_getChar(input) << 16;
	num += SWFInput_getChar(input) << 8;
	num += SWFInput_getChar(input);
	return num;
}

Project: libming

SWFInput_readSBits(SWFInput input, int number)
{
	int num = SWFInput_readBits(input, number);

	if(number && num & (1<<(number-1)))
		return num - (1<<number);
	else
		return num;
}

Project: libming

SWFInput_input_dtor(SWFInput input)
{
	free(input->data);
#if TRACK_ALLOCS
	ming_gc_remove_node(input->gcnode);
#endif
	free(input);
}

Project: libming

SWFInput_stream_read(SWFInput input, unsigned char* buffer, int count)
{
	struct SWFInputStreamData *data = (struct SWFInputStreamData *) input->data;
	int need = input->offset + count - input->length;

	if ( need > 0 )
	{
		int num;

		data->buffer =
			(unsigned char*) realloc(data->buffer,
							sizeof(unsigned char) * INPUTSTREAM_INCREMENT *
							(((input->offset + count) / INPUTSTREAM_INCREMENT) + 1));
		
		num = fread(data->buffer + input->length,
								sizeof(unsigned char), need, data->file);

		input->length += num;
	}

	if ( count > input->length - input->offset )
		count = input->length - input->offset;

	memcpy(buffer, data->buffer + input->offset, count);

	return count;
}

Project: libming

SWFInput_getUInt32(SWFInput input)
{
	unsigned long num = SWFInput_getChar(input);
	num += SWFInput_getChar(input) << 8;
	num += SWFInput_getChar(input) << 16;
	num += SWFInput_getChar(input) << 24;
	return num;
}

Project: libming

SWFInput_buffer_getChar(SWFInput input)
{
	if ( input->offset >= input->length )
		return EOF;
	else
		return ((unsigned char *)input->data)[input->offset++];
}

Project: libming

SWFInput_length(SWFInput input)
{
	int pos = SWFInput_tell(input);

	SWFInput_seek(input, 0, SEEK_END);
	SWFInput_seek(input, pos, SEEK_SET);

	return input->length;
}

Project: libming

newSWFInput_stream(FILE* f)
{
	SWFInput input = (SWFInput)malloc(sizeof(struct SWFInput_s));
	struct SWFInputStreamData *data;

	/* If malloc failed, return NULL to signify this */
	if (NULL == input)
		return NULL;

	data = (struct SWFInputStreamData *)malloc(sizeof(struct SWFInputStreamData));

	/* If malloc failed, free memory allocated for input and return NULL to signify the failure */
	if (NULL == data)
	{
		free(input);
		return NULL;
	}

	input->getChar = SWFInput_stream_getChar;
	input->destroy = SWFInput_stream_dtor;
	input->eof = SWFInput_stream_eof;
	input->read = SWFInput_stream_read;
	input->seek = SWFInput_stream_seek;

	input->offset = 0;
	input->length = 0;
	input->buffer = 0;
	input->bufbits = 0;

	data->file = f;
	data->buffer = NULL;

	input->data = (void *)data;

#if TRACK_ALLOCS
	input->gcnode = ming_gc_add_node(input, (dtorfunctype) destroySWFInput);
#endif

	return input;
}

Project: libming

SWFInput_buffer_eof(SWFInput input)
{
	return input->offset >= input->length;
}

Project: libming

SWFInput_file_seek(SWFInput input, long offset, int whence)
{
	if ( fseek((FILE *)input->data, offset, whence) == -1 )
	{
		if ( errno == EBADF )
			SWF_error("This is not a seekable stream- use newSWFInput_stream instead");
		else if ( errno == EINVAL )
			SWF_error("Invalid whence argument");
		else
			SWF_error("Unknown error");
	}

	if ( whence == SEEK_SET )
		input->offset = offset;

	else if ( whence == SEEK_END )
		input->offset = input->length - offset;

	else if ( whence == SEEK_CUR )
		input->offset += offset;
}

Project: libming

SWFInput_readBits(SWFInput input, int number)
{
	int ret = input->buffer;
	if ( number == input->bufbits )
	{
		input->bufbits = 0;
		input->buffer = 0;
		return ret;
	}

	if ( number > input->bufbits )
	{
		number -= input->bufbits;

		while( number > 8 )
		{
			ret <<= 8;
			ret += SWFInput_getChar(input);
			number -= 8;
		}
		
		input->buffer = SWFInput_getChar(input);
		
		if ( number > 0 )
		{
			ret <<= number;
			input->bufbits = 8-number;
			ret += input->buffer >> (8-number);
			input->buffer &= (1<<input->bufbits)-1;
		}

		return ret;
	}

	ret = input->buffer >> (input->bufbits-number);
	input->bufbits -= number;
	input->buffer &= (1<<input->bufbits)-1;
//	printf("done: readBits(%i) numer < bufbits %i\n", number, ret);
	return ret;
}

Project: libming

SWFInput_getUInt24_BE(SWFInput input)
{
	unsigned long num = SWFInput_getChar(input) << 16;
	num += SWFInput_getChar(input) << 8;
	num += SWFInput_getChar(input);
	return num;
}

Project: libming

newSWFInput_input(SWFInput in, unsigned int length)
{
	SWFInput input;
	struct SWFInputPtr *data;

	if (in == NULL)
		return NULL;

	input = (SWFInput)malloc(sizeof(struct SWFInput_s));

	/* If malloc failed, return NULL to signify this */
	if (NULL == input)
		return NULL;

	input->getChar = SWFInput_input_getChar;
	input->destroy = SWFInput_input_dtor;
	input->eof = SWFInput_input_eof;
	input->read = SWFInput_input_read;
	input->seek = SWFInput_input_seek;

	data = (struct SWFInputPtr *)malloc(sizeof(struct SWFInputPtr));

	/* If malloc failed, free memory allocated for input and return NULL to signify the failure */
	if (NULL == data)
	{
		free(input);
		return NULL;
	}

	data->offset = SWFInput_tell(in);
	data->input = in;

	input->offset = 0;
	input->length = length;

	input->data = (void *)data;
	input->buffer = 0;
	input->bufbits = 0;
#if TRACK_ALLOCS
	input->gcnode = ming_gc_add_node(input, (dtorfunctype) destroySWFInput);
#endif

	return input;
}

Project: libming

newSWFInput_bufferCopy(unsigned char *buffer, int length)
{
	SWFInput input;
	unsigned char *data = (unsigned char *)malloc(length);
	if(data == NULL)
		return NULL;

	memcpy(data, buffer, length);
	input = newSWFInput_buffer(data, length);
	input->destroy = SWFInput_buffer_dtor;
#if TRACK_ALLOCS
	input->gcnode = ming_gc_add_node(input, (dtorfunctype) destroySWFInput);
#endif
	return input;
}

Project: libming

SWFInput_rewind(SWFInput input)
{
	SWFInput_seek(input, 0, SEEK_SET);
}

Project: libming

newSWFInput_allocedBuffer(unsigned char *buffer, int length)
{
	SWFInput input = newSWFInput_buffer(buffer, length);
	input->destroy = SWFInput_buffer_dtor;
#if TRACK_ALLOCS
	input->gcnode = ming_gc_add_node(input, (dtorfunctype) destroySWFInput);
#endif
	return input;
}

Project: libming

SWFInput_read(SWFInput input, unsigned char* buffer, int count)
{
	return input->read(input, buffer, count);
}

Project: libming

newSWFInput_buffer(unsigned char* buffer, int length)
{
	SWFInput input = (SWFInput) malloc(sizeof(struct SWFInput_s));

	/* If malloc failed, return NULL to signify this */
	if (NULL == input)
		return NULL;

	input->getChar = SWFInput_buffer_getChar;
	input->destroy = SWFInput_dtor;
	input->eof = SWFInput_buffer_eof;
	input->read = SWFInput_buffer_read;
	input->seek = SWFInput_buffer_seek;
	input->data = buffer;
	input->buffer = 0;
	input->bufbits = 0;
	input->offset = 0;
	input->length = length;

#if TRACK_ALLOCS
	input->gcnode = ming_gc_add_node(input, (dtorfunctype) destroySWFInput);
#endif

	return input;
}

Project: libming

SWFInput_input_eof(SWFInput input)
{
	return input->offset >= input->length;
}

Project: libming

SWFInput_input_getChar(SWFInput input)
{
	struct SWFInputPtr *ptr;
 	int old_offset, result;

	if ( input->offset >= input->length )
		return EOF;
		
	ptr = (struct SWFInputPtr *)input->data;
	old_offset = SWFInput_tell(ptr->input);
 
	SWFInput_seek(ptr->input, ptr->offset + input->offset, SEEK_SET);
	result = SWFInput_getChar(ptr->input);
	input->offset++;
	SWFInput_seek(ptr->input, old_offset, SEEK_SET);

	return result;
}

Project: libming

SWFInput_getUInt16_BE(SWFInput input)
{
	int num = SWFInput_getChar(input) << 8;
	num += SWFInput_getChar(input);
	return num;
}

Project: libming

SWFInput_buffer_read(SWFInput input, unsigned char* buffer, int count)
{
	if ( count > input->length - input->offset )
		count = input->length - input->offset;

	memcpy(buffer, (unsigned char*)input->data + input->offset, count);
	input->offset += count;

	return count;
}

Project: libming

newSWFInput_file(FILE *f)
{
	SWFInput input;
	struct stat buf;

	/* XXX - doesn't check for validity of f.. */

	if ( fseek(f, 0, SEEK_CUR) == -1 )
		return newSWFInput_stream(f);

	if ( fstat(fileno(f), &buf) == -1 )
		SWF_error("Couldn't fstat filehandle in newSWFInput_file");;

	input = (SWFInput) malloc(sizeof(struct SWFInput_s));

	/* If malloc failed, return NULL to signify this */
	if (NULL == input)
		return NULL;

	input->getChar = SWFInput_file_getChar;
	input->destroy = SWFInput_dtor;
	input->eof = SWFInput_file_eof;
	input->seek = SWFInput_file_seek;
	input->read = SWFInput_file_read;
	input->data = f;
	input->bufbits = 0;
	input->buffer = 0;
	input->offset = 0;
	input->length = buf.st_size;

#if TRACK_ALLOCS
	input->gcnode = ming_gc_add_node(input, (dtorfunctype) destroySWFInput);
#endif

	return input;
}

Project: libming

static int SWFInput_stream_eof(SWFInput input)
{
	return ((input->offset >= input->length) ? feof((FILE *)input->data) : 0);
}

Project: libming

SWFInput_tell(SWFInput input)
{
	return input->offset;
}

Project: libming

SWFInput_buffer_dtor(SWFInput input)
{
	free(input->data);
#if TRACK_ALLOCS
	ming_gc_remove_node(input->gcnode);
#endif
	free(input);
}

Project: libming

SWFInput_input_read(SWFInput input, unsigned char* buffer, int count)
{
	int ret;
	struct SWFInputPtr *ptr;
	int old_offset;

	if ( count > input->length - input->offset )
		count = input->length - input->offset;

	ptr = (struct SWFInputPtr *)input->data;
        old_offset = SWFInput_tell(ptr->input);
	SWFInput_seek(ptr->input, ptr->offset + input->offset, SEEK_SET);

	ret = SWFInput_read((SWFInput)ptr->input, buffer, count);
	if(ret != count)
		SWF_warn("SWFInput_input_read: ret %i, count %i\n", ret, count);
	input->offset += count;

	SWFInput_seek(ptr->input, old_offset, SEEK_SET);

	return count;
}

Project: libming

SWFInput_getChar(SWFInput input)
{
	return input->getChar(input);
}

Project: libming

SWFInput_dtor(SWFInput input)
{
#if TRACK_ALLOCS
	ming_gc_remove_node(input->gcnode);
#endif
	free(input);
}

Project: libming

static void SWFInput_dtor_close(SWFInput input)
{
	fclose((FILE *)input->data);
	SWFInput_dtor(input);
}

Project: libming

newSWFInput_filename(const char *filename)
{
	FILE *file;
	SWFInput input;
	
	file = fopen(filename, "rb");
	if(file == NULL)
	{
		SWF_warn("newSWFInput_filename: %s: %s\n",
			filename, strerror(errno));
		return NULL;
	}

	input = newSWFInput_file(file);
	if(input == NULL)
		return NULL;

	input->destroy = SWFInput_dtor_close;
	return input;
}

Project: libming

SWFInput_stream_dtor(SWFInput input)
{
	free(input->data);
#if TRACK_ALLOCS
	ming_gc_remove_node(input->gcnode);
#endif
	free(input);
}

Project: libming

SWFInput_buffer_seek(SWFInput input, long offset, int whence)
{
	if ( whence == SEEK_CUR )
	{
		if ( offset >= 0 )
			input->offset = min(input->length, input->offset + offset);
		else
			input->offset = max(0, input->offset + offset);
	}

	else if ( whence == SEEK_END )
		input->offset = max(0, input->length - offset);

	else if ( whence == SEEK_SET )
		input->offset = min(input->length, offset);
}

Project: libming

SWFInput_getSInt16(SWFInput input)
{
	int num = SWFInput_getChar(input);
	num += SWFInput_getChar(input) * 256;
	return num;
}

Project: libming

SWFInput_file_eof(SWFInput input)
{
	return feof((FILE *)input->data);
}

Project: libming

SWFInput_getUInt16(SWFInput input)
{
	int num = SWFInput_getChar(input);
	num += SWFInput_getChar(input) << 8;
	return num;
}

Project: libming

static int SWFInput_file_read(SWFInput input, unsigned char *buffer, int count)
{	int len = fread(buffer, 1, count, (FILE *)input->data);
	input->offset += len;
	return len;
}

Project: libming

SWFInput_input_seek(SWFInput input, long offset, int whence)
{
	if ( whence == SEEK_CUR )
	{
		if ( offset >= 0 )
			input->offset = min(input->length, input->offset + offset);
		else
			input->offset = max(0, input->offset + offset);
	}

	else if ( whence == SEEK_END )
		input->offset = max(0, input->length - offset);

	else if ( whence == SEEK_SET )
		input->offset = min(input->length, offset);
}

Project: libming

SWFInput_file_getChar(SWFInput input)
{
	int c = fgetc((FILE *)input->data);

	if ( c == EOF )
		input->length = input->offset;
	else
		++input->offset;

	return c;
}

Project: libming

SWFInput_eof(SWFInput input)
{
	return input->eof(input);
}

Project: libming

destroySWFInput(SWFInput input)
{
	input->destroy(input);
}