Commit message: The memory stream interface allows for a buffer size of zero.
The case of a zero-sized buffer was not handled correctly, as it could
lead to a double free.
This problem has now been fixed (hopefully).
One might ask whether a zero-sized buffer should be allowed at all,
but this is a question for another day.
CVE: CVE-2016-8693
CWE: CWE-415
Vulnerability description: Double free vulnerability in the mem_close function in jas_stream.c in JasPer before 1.900.10 allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted BMP image to the imginfo command.

Project: jasper
File: jas_stream.c

static int file_close(jas_stream_obj_t *obj)
{
	jas_stream_fileobj_t *fileobj = JAS_CAST(jas_stream_fileobj_t *, obj);
	int ret;
	ret = close(fileobj->fd);
	if (fileobj->flags & JAS_STREAM_FILEOBJ_DELONCLOSE) {
		unlink(fileobj->pathname);
	}
	jas_free(fileobj);
	return ret;
}

Project: jasper
File: jas_stream.c

static int file_read(jas_stream_obj_t *obj, char *buf, int cnt)
{
	jas_stream_fileobj_t *fileobj = JAS_CAST(jas_stream_fileobj_t *, obj);
	return read(fileobj->fd, buf, cnt);
}

Project: jasper
File: jas_stream.c

static long file_seek(jas_stream_obj_t *obj, long offset, int origin)
{
	jas_stream_fileobj_t *fileobj = JAS_CAST(jas_stream_fileobj_t *, obj);
	return lseek(fileobj->fd, offset, origin);
}

Project: jasper
File: jas_stream.c

static int file_write(jas_stream_obj_t *obj, char *buf, int cnt)
{
	jas_stream_fileobj_t *fileobj = JAS_CAST(jas_stream_fileobj_t *, obj);
	return write(fileobj->fd, buf, cnt);
}

Project: jasper
File: jas_stream.c

static void jas_stream_destroy(jas_stream_t *stream)
{
	/* If the memory for the buffer was allocated with malloc, free
	this memory. */
	if ((stream->bufmode_ & JAS_STREAM_FREEBUF) && stream->bufbase_) {
		jas_free(stream->bufbase_);
		stream->bufbase_ = 0;
	}
	jas_free(stream);
}

Project: jasper
File: jas_stream.c

jas_stream_t *jas_stream_memopen(char *buf, int bufsize)
{
	jas_stream_t *stream;
	jas_stream_memobj_t *obj;

	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 <= 0) {
		obj->bufsize_ = 1024;
		obj->growable_ = 1;
	} else {
		obj->bufsize_ = bufsize;
		obj->growable_ = 0;
	}
	if (buf) {
		obj->buf_ = (unsigned char *) buf;
	} else {
		obj->buf_ = jas_malloc(obj->bufsize_);
		obj->myalloc_ = 1;
	}
	if (!obj->buf_) {
		jas_stream_close(stream);
		return 0;
	}

	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;
}

Project: jasper
File: jas_stream.c

static int mem_close(jas_stream_obj_t *obj)
{
	jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
	if (m->myalloc_ && m->buf_) {
		jas_free(m->buf_);
		m->buf_ = 0;
	}
	jas_free(obj);
	return 0;
}

Project: jasper
File: jas_stream.c

static int mem_read(jas_stream_obj_t *obj, char *buf, int cnt)
{
	int n;
	assert(cnt >= 0);
	assert(buf);
	jas_stream_memobj_t *m = (jas_stream_memobj_t *)obj;
	n = m->len_ - m->pos_;
	cnt = JAS_MIN(n, cnt);
	memcpy(buf, &m->buf_[m->pos_], cnt);
	m->pos_ += cnt;
	return cnt;
}

Project: jasper
File: jas_stream.c

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;

	switch (origin) {
	case SEEK_SET:
		newpos = offset;
		break;
	case SEEK_END:
		newpos = m->len_ - offset;
		break;
	case SEEK_CUR:
		newpos = m->pos_ + offset;
		break;
	default:
		abort();
		break;
	}
	if (newpos < 0) {
		return -1;
	}
	m->pos_ = newpos;

	return m->pos_;
}

Project: jasper
File: jas_stream.c

static int sfile_read(jas_stream_obj_t *obj, char *buf, int cnt)
{
	FILE *fp;
	fp = JAS_CAST(FILE *, obj);
	return fread(buf, 1, cnt, fp);
}

Project: jasper
File: jas_stream.c

static long sfile_seek(jas_stream_obj_t *obj, long offset, int origin)
{
	FILE *fp;
	fp = JAS_CAST(FILE *, obj);
	return fseek(fp, offset, origin);
}

Project: jasper
File: jas_stream.c

static int sfile_write(jas_stream_obj_t *obj, char *buf, int cnt)
{
	FILE *fp;
	fp = JAS_CAST(FILE *, obj);
	return fwrite(buf, 1, cnt, fp);
}