CVE-2016-9262 (published 2017-03-23T18:59Z)

Multiple integer overflows in the (1) jas_realloc function in base/jas_malloc.c and (2) mem_resize function in base/jas_stream.c in JasPer before 1.900.22 allow remote attackers to cause a denial of service via a crafted image, which triggers use after free vulnerabilities.

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 5.5, MEDIUM (CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-190
cwe_name: Integer Overflow or Wraparound
description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
extended_description: 
            
            
            
            
         
         

Fix information:
repo_url: https://github.com/mdadams/jasper
commit_details: repo_url: https://github.com/mdadams/jasper
author: Michael Adams
author_date: 2016-11-06 10:26:16-08:00
msg: Made some changes to the I/O stream library for memory streams.
There were a number of potential problems due to the possibility
of integer overflow.
Changed some integral types to the larger types size_t or ssize_t.
For example, the function mem_resize now takes the buffer size parameter
as a size_t.
Added a new function jas_stream_memopen2, which takes a
buffer size specified as a size_t instead of an int.
This can be used in jas_image_cmpt_create to avoid potential
overflow problems.

Added a new function jas_deprecated to warn about reliance on
deprecated library behavior.
repository: repo_url: https://github.com/mdadams/jasper
repo_name: jasper-software/jasper
description: Official Repository for the JasPer Image Coding Toolkit
repo_language: C
owner: mdadams
file_changes: filename: jas_debug.c
change_type: ModificationType.MODIFY
diff: @@ -135,3 +135,22 @@ int jas_memdump(FILE *out, void *data, size_t len)
 	}
 	return 0;
 }
+
+/******************************************************************************\
+* Code.
+\******************************************************************************/
+
+void jas_deprecated(const char *s)
+{
+	static char message[] =
+	"WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!!\n"
+	"WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!!\n"
+	"WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!! WARNING!!!\n"
+	"YOUR CODE IS RELYING ON DEPRECATED FUNCTIONALTIY IN THE JASPER LIBRARY.\n"
+	"THIS FUNCTIONALITY WILL BE REMOVED IN THE NEAR FUTURE.\n"
+	"PLEASE FIX THIS PROBLEM BEFORE YOUR CODE STOPS WORKING!\n"
+	;
+	jas_eprintf("%s", message);
+	jas_eprintf("The specific problem is as follows:\n%s\n", s);
+	//abort();
+}

programming_language: C
filename: jas_image.c
change_type: ModificationType.MODIFY
diff: @@ -347,7 +347,7 @@ static jas_image_cmpt_t *jas_image_cmpt_create(int_fast32_t tlx,
 	  !jas_safe_size_mul(size, cmpt->cps_, &size)) {
 		goto error;
 	}
-	cmpt->stream_ = (inmem) ? jas_stream_memopen(0, size) :
+	cmpt->stream_ = (inmem) ? jas_stream_memopen2(0, size) :
 	  jas_stream_tmpfile();
 	if (!cmpt->stream_) {
 		goto error;

programming_language: C++
filename: jas_stream.c
change_type: ModificationType.MODIFY
diff: @@ -169,6 +169,10 @@ static jas_stream_t *jas_stream_create()
 	return stream;
 }
 
+#if 0
+
+/* Obsolete code. */
+
 jas_stream_t *jas_stream_memopen(char *buf, int bufsize)
 {
 	jas_stream_t *stream;
@@ -238,6 +242,136 @@ jas_stream_t *jas_stream_memopen(char *buf, int bufsize)
 	return stream;
 }
 
+#else
+
+/*
+This function will eventually replace jas_stream_memopen.
+If buf is 0 and bufsize > 0:
+	a buffer is dynamically allocated with size bufsize and this buffer is
+	not growable.
+If buf is 0 and bufsize is 0:
+	a buffer is dynamically allocated whose size will automatically grow to
+	accommodate the amount of data written.
+If buf is not 0:
+	bufsize (which, in this case, is not currently allowed to be zero) is
+	the size of the (nongrowable) buffer pointed to by buf.
+*/
+
+jas_stream_t *jas_stream_memopen2(char *buf, size_t bufsize)
+{
+	jas_stream_t *stream;
+	jas_stream_memobj_t *obj;
+
+	JAS_DBGLOG(100, ("jas_stream_memopen2(%p, %zu)\n", buf, bufsize));
+
+	assert((buf && bufsize > 0) || (!buf));
+
+	if (!(stream = jas_stream_create())) {
+		return 0;
+	}
+
+	/* A stream associated with a memory buffer is always opened
+	for both reading and writing in binary mode. */
+	stream->openmode_ = JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
+
+	/* Since the stream data is already resident in memory, buffering
+	is not necessary. */
+	/* But... It still may be faster to use buffering anyways. */
+	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
+
+	/* Select the operations for a memory stream. */
+	stream->ops_ = &jas_stream_memops;
+
+	/* Allocate memory for the underlying memory stream object. */
+	if (!(obj = jas_malloc(sizeof(jas_stream_memobj_t)))) {
+		jas_stream_destroy(stream);
+		return 0;
+	}
+	stream->obj_ = (void *) obj;
+
+	/* Initialize a few important members of the memory stream object. */
+	obj->myalloc_ = 0;
+	obj->buf_ = 0;
+
+	/* If the buffer size specified is nonpositive, then the buffer
+	is allocated internally and automatically grown as needed. */
+	if (!bufsize) {
+		obj->bufsize_ = 1024;
+		obj->growable_ = 1;
+	} else {
+		obj->bufsize_ = bufsize;
+		obj->growable_ = 0;
+	}
+	if (buf) {
+		obj->buf_ = JAS_CAST(unsigned char *, buf);
+	} else {
+		obj->buf_ = jas_malloc(obj->bufsize_);
+		obj->myalloc_ = 1;
+	}
+	if (!obj->buf_) {
+		jas_stream_close(stream);
+		return 0;
+	}
+	JAS_DBGLOG(100, ("jas_stream_memopen2 buffer buf=%p myalloc=%d\n",
+	  obj->buf_, obj->myalloc_));
+
+	if (bufsize > 0 && buf) {
+		/* If a buffer was supplied by the caller and its length is positive,
+		  make the associated buffer data appear in the stream initially. */
+		obj->len_ = bufsize;
+	} else {
+		/* The stream is initially empty. */
+		obj->len_ = 0;
+	}
+	obj->pos_ = 0;
+	
+	return stream;
+}
+
+/*
+NOTE:
+The version of the function jas_stream_memopen only exists for backwards
+compatibility.
+Eventually, it should be replaced by jas_stream_memopen2.
+In retrospect, it was a very poor choice to have specified the buffer
+size parameter (bufsize) to have type int.  On some machines, int may only
+be a 16-bit integer.  This precludes larger-sized buffer allocations, which
+are needed in practice.
+
+If bufsize <= 0, the buffer is growable; otherwise, the buffer has a fixed
+size of bufsize.
+If buf is 0, the buffer is dynamically allocated with jas_malloc.
+If buf is not 0 and bufsize <= 0 (which is not permitted in any
+circumstances), bad things will happen (especially if the buf was not
+allocated with jas_malloc).
+*/
+jas_stream_t *jas_stream_memopen(char *buf, int bufsize)
+{
+	char *new_buf;
+	size_t new_bufsize;
+
+	JAS_DBGLOG(100, ("jas_stream_memopen(%p, %d)\n", buf, bufsize));
+	if (bufsize < 0) {
+		jas_deprecated("negative buffer size for jas_stream_memopen");
+	}
+	if (buf && bufsize <= 0) {
+		// This was never a valid thing to do with the old API.
+		jas_eprintf("Invalid use of jas_stream_memopen detected.\n");
+		jas_deprecated("A user-provided buffer for "
+		  "jas_stream_memopen cannot be growable.\n");
+	}
+	if (bufsize <= 0) {
+		new_bufsize = 0;
+		new_buf = 0;
+	} else {
+		new_bufsize = bufsize;
+		new_buf = buf;
+	}
+	return jas_stream_memopen2(new_buf, new_bufsize);
+}
+
+#endif
+
 jas_stream_t *jas_stream_fopen(const char *filename, const char *mode)
 {
 	jas_stream_t *stream;
@@ -520,6 +654,10 @@ int jas_stream_read(jas_stream_t *stream, void *buf, int cnt)
 	int c;
 	char *bufptr;
 
+	if (cnt < 0) {
+		jas_deprecated("negative count for jas_stream_read");
+	}
+
 	bufptr = buf;
 
 	n = 0;
@@ -539,6 +677,10 @@ int jas_stream_write(jas_stream_t *stream, const void *buf, int cnt)
 	int n;
 	const char *bufptr;
 
+	if (cnt < 0) {
+		jas_deprecated("negative count for jas_stream_write");
+	}
+
 	bufptr = buf;
 
 	n = 0;
@@ -604,6 +746,9 @@ char *jas_stream_gets(jas_stream_t *stream, char *buf, int bufsize)
 int jas_stream_gobble(jas_stream_t *stream, int n)
 {
 	int m;
+	if (n < 0) {
+		jas_deprecated("negative count for jas_stream_gobble");
+	}
 	m = n;
 	for (m = n; m > 0; --m) {
 		if (jas_stream_getc(stream) == EOF) {
@@ -616,6 +761,9 @@ int jas_stream_gobble(jas_stream_t *stream, int n)
 int jas_stream_pad(jas_stream_t *stream, int n, int c)
 {
 	int m;
+	if (n < 0) {
+		jas_deprecated("negative count for jas_stream_pad");
+	}
 	m = n;
 	for (m = n; m > 0; --m) {
 		if (jas_stream_putc(stream, c) == EOF)
@@ -988,7 +1136,7 @@ long jas_stream_length(jas_stream_t *stream)
 
 static int mem_read(jas_stream_obj_t *obj, char *buf, int cnt)
 {
-	int n;
+	ssize_t n;
 	assert(cnt >= 0);
 	assert(buf);
 
@@ -1001,14 +1149,21 @@ static int mem_read(jas_stream_obj_t *obj, char *buf, int cnt)
 	return cnt;
 }
 
-static int mem_resize(jas_stream_memobj_t *m, int bufsize)
+static int mem_resize(jas_stream_memobj_t *m, size_t bufsize)
 {
 	unsigned char *buf;
 
 	//assert(m->buf_);
-	assert(bufsize >= 0);
+	//assert(bufsize >= 0);
+
+	JAS_DBGLOG(100, ("mem_resize(%p, %zu)\n", m, bufsize));
+	if (!bufsize) {
+		jas_eprintf(
+		  "mem_resize was not really designed to handle a buffer of size 0\n"
+		  "This may not work.\n"
+		);
+	}
 
-	JAS_DBGLOG(100, ("mem_resize(%p, %d)\n", m, bufsize));
 	if (!(buf = jas_realloc2(m->buf_, bufsize, sizeof(unsigned char))) &&
 	  bufsize) {
 		JAS_DBGLOG(100, ("mem_resize realloc failed\n"));
@@ -1022,11 +1177,11 @@ static int mem_resize(jas_stream_memobj_t *m, int bufsize)
 
 static int mem_write(jas_stream_obj_t *obj, char *buf, int cnt)
 {
-	int n;
+	size_t n;
 	int ret;
 	jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
-	long newbufsize;
-	long newpos;
+	size_t newbufsize;
+	size_t newpos;
 
 	assert(buf);
 	assert(cnt >= 0);
@@ -1036,13 +1191,15 @@ static int mem_write(jas_stream_obj_t *obj, char *buf, int cnt)
 	if (newpos > m->bufsize_ && m->growable_) {
 		newbufsize = m->bufsize_;
 		while (newbufsize < newpos) {
-			newbufsize <<= 1;
-			assert(newbufsize >= 0);
+			//newbufsize <<= 1;
+			if (!jas_safe_size_mul(newbufsize, 2, &newbufsize)) {
+				JAS_DBGLOG(100, ("new buffer size would cause overflow\n"));
+				return -1;
+			}
 		}
-		JAS_DBGLOG(100, ("mem_write resizing from %d to %z\n", m->bufsize_,
+		JAS_DBGLOG(100, ("mem_write resizing from %d to %zu\n", m->bufsize_,
 		  newbufsize));
-		JAS_DBGLOG(100, ("mem_write resizing from %d to %ul\n", m->bufsize_,
-		  JAS_CAST(unsigned long, newbufsize)));
+		assert(newbufsize > 0);
 		if (mem_resize(m, newbufsize)) {
 			return -1;
 		}
@@ -1076,7 +1233,7 @@ static int mem_write(jas_stream_obj_t *obj, char *buf, int cnt)
 static long mem_seek(jas_stream_obj_t *obj, long offset, int origin)
 {
 	jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
-	long newpos;
+	size_t newpos;
 
 	JAS_DBGLOG(100, ("mem_seek(%p, %ld, %d)\n", obj, offset, origin));
 	switch (origin) {

programming_language: C
filename: jas_debug.h
change_type: ModificationType.MODIFY
diff: @@ -107,6 +107,9 @@ int jas_eprintf(const char *fmt, ...);
 /* Dump memory to a stream. */
 int jas_memdump(FILE *out, void *data, size_t len);
 
+/* Warn about use of deprecated functionality. */
+void jas_deprecated(const char *s);
+
 #ifdef __cplusplus
 }
 #endif

programming_language: C
filename: jas_stream.h
change_type: ModificationType.MODIFY
diff: @@ -267,7 +267,7 @@ typedef struct {
 	uchar *buf_;
 
 	/* The allocated size of the buffer for holding file data. */
-	int bufsize_;
+	size_t bufsize_;
 
 	/* The length of the file. */
 	int_fast32_t len_;
@@ -293,6 +293,10 @@ jas_stream_t *jas_stream_fopen(const char *filename, const char *mode);
 /* Open a memory buffer as a stream. */
 jas_stream_t *jas_stream_memopen(char *buf, int bufsize);
 
+/* Do not use this function.
+It will eventually replace jas_stream_memopen. */
+jas_stream_t *jas_stream_memopen2(char *buf, size_t bufsize);
+
 /* Open a file descriptor as a stream. */
 jas_stream_t *jas_stream_fdopen(int fd, const char *mode);
 

programming_language: C++