CVE-2016-9262: Integer Overflow or Wraparound (CWE-190)
value: 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.
CWE 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.
CVSS v3 base score: 5.5
Published: 2017-03-23T18:59Z
Repository: https://github.com/mdadams/jasper
Language: C
Files changed: src/libjasper/base/jas_debug.c, src/libjasper/base/jas_image.c, src/libjasper/base/jas_stream.c, src/libjasper/include/jasper/jas_debug.h, src/libjasper/include/jasper/jas_stream.h
Commit date: 2016-11-06 10:26:16 -0800
Commit message: 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.

Diff: diff --git a/src/libjasper/base/jas_debug.c b/src/libjasper/base/jas_debug.c
index d082f70..42a7480 100644
--- a/src/libjasper/base/jas_debug.c
+++ b/src/libjasper/base/jas_debug.c
@@ -133,5 +133,24 @@ int jas_memdump(FILE *out, void *data, size_t len)
 		}
 		fprintf(out, "\n");
 	}
 	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();
+}
diff --git a/src/libjasper/base/jas_image.c b/src/libjasper/base/jas_image.c
index d011bc5..54702b4 100644
--- a/src/libjasper/base/jas_image.c
+++ b/src/libjasper/base/jas_image.c
@@ -345,11 +345,11 @@ static jas_image_cmpt_t *jas_image_cmpt_create(int_fast32_t tlx,
 	// size = cmpt->width_ * cmpt->height_ * cmpt->cps_;
 	if (!jas_safe_size_mul(cmpt->width_, cmpt->height_, &size) ||
 	  !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;
 	}
 
diff --git a/src/libjasper/base/jas_stream.c b/src/libjasper/base/jas_stream.c
index e3918b9..cb0c894 100644
--- a/src/libjasper/base/jas_stream.c
+++ b/src/libjasper/base/jas_stream.c
@@ -167,10 +167,14 @@ static jas_stream_t *jas_stream_create()
 	stream->rwlimit_ = -1;
 
 	return stream;
 }
 
+#if 0
+
+/* Obsolete code. */
+
 jas_stream_t *jas_stream_memopen(char *buf, int bufsize)
 {
 	jas_stream_t *stream;
 	jas_stream_memobj_t *obj;
 
@@ -236,10 +240,140 @@ jas_stream_t *jas_stream_memopen(char *buf, int bufsize)
 	obj->pos_ = 0;
 	
 	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;
 	jas_stream_fileobj_t *obj;
 	int openflags;
@@ -518,10 +652,14 @@ int jas_stream_read(jas_stream_t *stream, void *buf, int cnt)
 {
 	int n;
 	int c;
 	char *bufptr;
 
+	if (cnt < 0) {
+		jas_deprecated("negative count for jas_stream_read");
+	}
+
 	bufptr = buf;
 
 	n = 0;
 	while (n < cnt) {
 		if ((c = jas_stream_getc(stream)) == EOF) {
@@ -537,10 +675,14 @@ int jas_stream_read(jas_stream_t *stream, void *buf, int cnt)
 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;
 	while (n < cnt) {
 		if (jas_stream_putc(stream, *bufptr) == EOF) {
@@ -602,10 +744,13 @@ 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) {
 			return n - m;
 		}
@@ -614,10 +759,13 @@ 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)
 			return n - m;
 	}
@@ -986,11 +1134,11 @@ long jas_stream_length(jas_stream_t *stream)
 * Memory stream object.
 \******************************************************************************/
 
 static int mem_read(jas_stream_obj_t *obj, char *buf, int cnt)
 {
-	int n;
+	ssize_t n;
 	assert(cnt >= 0);
 	assert(buf);
 
 	JAS_DBGLOG(100, ("mem_read(%p, %p, %d)\n", obj, buf, cnt));
 	jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
@@ -999,18 +1147,25 @@ static int mem_read(jas_stream_obj_t *obj, char *buf, int cnt)
 	memcpy(buf, &m->buf_[m->pos_], cnt);
 	m->pos_ += 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"));
 		return -1;
 	}
@@ -1020,31 +1175,33 @@ static int mem_resize(jas_stream_memobj_t *m, int bufsize)
 	return 0;
 }
 
 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);
 
 	JAS_DBGLOG(100, ("mem_write(%p, %p, %d)\n", obj, buf, cnt));
 	newpos = m->pos_ + 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;
 		}
 	}
 	if (m->pos_ > m->len_) {
@@ -1074,11 +1231,11 @@ 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) {
 	case SEEK_SET:
 		newpos = offset;
diff --git a/src/libjasper/include/jasper/jas_debug.h b/src/libjasper/include/jasper/jas_debug.h
index 1619a5a..6b78112 100644
--- a/src/libjasper/include/jasper/jas_debug.h
+++ b/src/libjasper/include/jasper/jas_debug.h
@@ -105,10 +105,13 @@ int jas_setdbglevel(int dbglevel);
 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
 
 #endif
diff --git a/src/libjasper/include/jasper/jas_stream.h b/src/libjasper/include/jasper/jas_stream.h
index 651bea9..b9801d8 100644
--- a/src/libjasper/include/jasper/jas_stream.h
+++ b/src/libjasper/include/jasper/jas_stream.h
@@ -265,11 +265,11 @@ typedef struct {
 
 	/* The data associated with this file. */
 	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_;
 
 	/* The seek position. */
@@ -291,10 +291,14 @@ typedef struct {
 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);
 
 /* Open a stdio stream as a stream. */
 jas_stream_t *jas_stream_freopen(const char *path, const char *mode, FILE *fp);

Vulnerable code: 	cmpt->stream_ = (inmem) ? jas_stream_memopen(0, size) :
	int n;
static int mem_resize(jas_stream_memobj_t *m, int bufsize)
	assert(bufsize >= 0);
	JAS_DBGLOG(100, ("mem_resize(%p, %d)\n", m, bufsize));
	int n;
	long newbufsize;
	long newpos;
			newbufsize <<= 1;
			assert(newbufsize >= 0);
		JAS_DBGLOG(100, ("mem_write resizing from %d to %z\n", m->bufsize_,
		JAS_DBGLOG(100, ("mem_write resizing from %d to %ul\n", m->bufsize_,
		  JAS_CAST(unsigned long, newbufsize)));
	long newpos;
	int bufsize_;
Fixed code: 
/******************************************************************************\
* 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();
}
	cmpt->stream_ = (inmem) ? jas_stream_memopen2(0, size) :
#if 0

/* Obsolete code. */

#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

	if (cnt < 0) {
		jas_deprecated("negative count for jas_stream_read");
	}

	if (cnt < 0) {
		jas_deprecated("negative count for jas_stream_write");
	}

	if (n < 0) {
		jas_deprecated("negative count for jas_stream_gobble");
	}
	if (n < 0) {
		jas_deprecated("negative count for jas_stream_pad");
	}
	ssize_t n;
static int mem_resize(jas_stream_memobj_t *m, size_t bufsize)
	//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"
		);
	}
	size_t n;
	size_t newbufsize;
	size_t newpos;
			//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 %zu\n", m->bufsize_,
		assert(newbufsize > 0);
	size_t newpos;
/* Warn about use of deprecated functionality. */
void jas_deprecated(const char *s);

	size_t bufsize_;
/* Do not use this function.
It will eventually replace jas_stream_memopen. */
jas_stream_t *jas_stream_memopen2(char *buf, size_t bufsize);