static int php_iconv_stream_filter_append_bucket(
		php_iconv_stream_filter *self,
		php_stream *stream, php_stream_filter *filter,
		php_stream_bucket_brigade *buckets_out,
		const char *ps, size_t buf_len, size_t *consumed,
		int persistent TSRMLS_DC)
{
	php_stream_bucket *new_bucket;
	char *out_buf = NULL;
	size_t out_buf_size;
	char *pd, *pt;
	size_t ocnt, prev_ocnt, icnt, tcnt;
	size_t initial_out_buf_size;

	if (ps == NULL) {
		initial_out_buf_size = 64;
		icnt = 1;
	} else {
		initial_out_buf_size = buf_len;
		icnt = buf_len;
	}

	out_buf_size = ocnt = prev_ocnt = initial_out_buf_size;
	if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
		return FAILURE;
	}

	pd = out_buf;

	if (self->stub_len > 0) {
		pt = self->stub;
		tcnt = self->stub_len;

		while (tcnt > 0) {
			if (iconv(self->cd, &pt, &tcnt, &pd, &ocnt) == (size_t)-1) {
#if ICONV_SUPPORTS_ERRNO
				switch (errno) {
					case EILSEQ:
						php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
						goto out_failure;

					case EINVAL:
						if (ps != NULL) {
							if (icnt > 0) {
								if (self->stub_len >= sizeof(self->stub)) {
									php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
									goto out_failure;
								}
								self->stub[self->stub_len++] = *(ps++);
								icnt--;
								pt = self->stub;
								tcnt = self->stub_len;
							} else {
								tcnt = 0;
								break;
							}
						} else {
						    php_error_docref(NULL, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
						    goto out_failure;
						}
						break;

					case E2BIG: {
						char *new_out_buf;
						size_t new_out_buf_size;

						new_out_buf_size = out_buf_size << 1;

						if (new_out_buf_size < out_buf_size) {
							/* whoa! no bigger buckets are sold anywhere... */
							if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
								goto out_failure;
							}

							php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);

							out_buf_size = ocnt = initial_out_buf_size;
							if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
								return FAILURE;
							}
							pd = out_buf;
						} else {
							if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
								if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
									goto out_failure;
								}

								php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
								return FAILURE;
							}
							pd = new_out_buf + (pd - out_buf);
							ocnt += (new_out_buf_size - out_buf_size);
							out_buf = new_out_buf;
							out_buf_size = new_out_buf_size;
						}
					} break;

					default:
						php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
						goto out_failure;
				}
#else
				if (ocnt == prev_ocnt) {
					php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
					goto out_failure;
				}
#endif
			}
			prev_ocnt = ocnt;
		}
		memmove(self->stub, pt, tcnt);
		self->stub_len = tcnt;
	}

	while (icnt > 0) {
		if ((ps == NULL ? iconv(self->cd, NULL, NULL, &pd, &ocnt):
					iconv(self->cd, (char **)&ps, &icnt, &pd, &ocnt)) == (size_t)-1) {
#if ICONV_SUPPORTS_ERRNO
			switch (errno) {
				case EILSEQ:
					php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): invalid multibyte sequence", self->from_charset, self->to_charset);
					goto out_failure;

				case EINVAL:
					if (ps != NULL) {
						if (icnt > sizeof(self->stub)) {
							php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): insufficient buffer", self->from_charset, self->to_charset);
							goto out_failure;
						}
						memcpy(self->stub, ps, icnt);
						self->stub_len = icnt;
						ps += icnt;
						icnt = 0;
					} else {
						php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unexpected octet values", self->from_charset, self->to_charset);
						goto out_failure;
					}
					break;

				case E2BIG: {
					char *new_out_buf;
					size_t new_out_buf_size;

					new_out_buf_size = out_buf_size << 1;

					if (new_out_buf_size < out_buf_size) {
						/* whoa! no bigger buckets are sold anywhere... */
						if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
							goto out_failure;
						}

						php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);

						out_buf_size = ocnt = initial_out_buf_size;
						if (NULL == (out_buf = pemalloc(out_buf_size, persistent))) {
							return FAILURE;
						}
						pd = out_buf;
					} else {
						if (NULL == (new_out_buf = perealloc(out_buf, new_out_buf_size, persistent))) {
							if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
								goto out_failure;
							}

							php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
							return FAILURE;
						}
						pd = new_out_buf + (pd - out_buf);
						ocnt += (new_out_buf_size - out_buf_size);
						out_buf = new_out_buf;
						out_buf_size = new_out_buf_size;
					}
				} break;

				default:
					php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
					goto out_failure;
			}
#else
			if (ocnt == prev_ocnt) {
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "iconv stream filter (\"%s\"=>\"%s\"): unknown error", self->from_charset, self->to_charset);
				goto out_failure;
			}
#endif
		} else {
			if (ps == NULL) {
				break;
			}
		}
		prev_ocnt = ocnt;
	}

	if (out_buf_size - ocnt > 0) {
		if (NULL == (new_bucket = php_stream_bucket_new(stream, out_buf, (out_buf_size - ocnt), 1, persistent TSRMLS_CC))) {
			goto out_failure;
		}
		php_stream_bucket_append(buckets_out, new_bucket TSRMLS_CC);
	} else {
		pefree(out_buf, persistent);
	}
	*consumed += buf_len - icnt;

	return SUCCESS;

out_failure:
	pefree(out_buf, persistent);
	return FAILURE;
}


THRIFT-3231 CPP: Limit recursion depth to 64
Client: cpp
Patch: Ben Craig <bencraig@apache.org>

uint32_t skip(Protocol_& prot, TType type) {
  switch (type) {
  case T_BOOL: {
    bool boolv;
    return prot.readBool(boolv);
  }
  case T_BYTE: {
    int8_t bytev;
    return prot.readByte(bytev);
  }
  case T_I16: {
    int16_t i16;
    return prot.readI16(i16);
  }
  case T_I32: {
    int32_t i32;
    return prot.readI32(i32);
  }
  case T_I64: {
    int64_t i64;
    return prot.readI64(i64);
  }
  case T_DOUBLE: {
    double dub;
    return prot.readDouble(dub);
  }
  case T_STRING: {
    std::string str;
    return prot.readBinary(str);
  }
  case T_STRUCT: {
    uint32_t result = 0;
    std::string name;
    int16_t fid;
    TType ftype;
    result += prot.readStructBegin(name);
    while (true) {
      result += prot.readFieldBegin(name, ftype, fid);
      if (ftype == T_STOP) {
        break;
      }
      result += skip(prot, ftype);
      result += prot.readFieldEnd();
    }
    result += prot.readStructEnd();
    return result;
  }
  case T_MAP: {
    uint32_t result = 0;
    TType keyType;
    TType valType;
    uint32_t i, size;
    result += prot.readMapBegin(keyType, valType, size);
    for (i = 0; i < size; i++) {
      result += skip(prot, keyType);
      result += skip(prot, valType);
    }
    result += prot.readMapEnd();
    return result;
  }
  case T_SET: {
    uint32_t result = 0;
    TType elemType;
    uint32_t i, size;
    result += prot.readSetBegin(elemType, size);
    for (i = 0; i < size; i++) {
      result += skip(prot, elemType);
    }
    result += prot.readSetEnd();
    return result;
  }
  case T_LIST: {
    uint32_t result = 0;
    TType elemType;
    uint32_t i, size;
    result += prot.readListBegin(elemType, size);
    for (i = 0; i < size; i++) {
      result += skip(prot, elemType);
    }
    result += prot.readListEnd();
    return result;
  }
  case T_STOP:
  case T_VOID:
  case T_U64:
  case T_UTF8:
  case T_UTF16:
    break;
  }
  return 0;
}

The client libraries in Apache Thrift before 0.9.3 might allow remote authenticated users to cause a denial of service (infinite recursion) via vectors involving the skip function.


uint32_t skip(Protocol_& prot, TType type) {
  TRecursionTracker tracker(prot);

  switch (type) {
  case T_BOOL: {
    bool boolv;
    return prot.readBool(boolv);
  }
  case T_BYTE: {
    int8_t bytev;
    return prot.readByte(bytev);
  }
  case T_I16: {
    int16_t i16;
    return prot.readI16(i16);
  }
  case T_I32: {
    int32_t i32;
    return prot.readI32(i32);
  }
  case T_I64: {
    int64_t i64;
    return prot.readI64(i64);
  }
  case T_DOUBLE: {
    double dub;
    return prot.readDouble(dub);
  }
  case T_STRING: {
    std::string str;
    return prot.readBinary(str);
  }
  case T_STRUCT: {
    uint32_t result = 0;
    std::string name;
    int16_t fid;
    TType ftype;
    result += prot.readStructBegin(name);
    while (true) {
      result += prot.readFieldBegin(name, ftype, fid);
      if (ftype == T_STOP) {
        break;
      }
      result += skip(prot, ftype);
      result += prot.readFieldEnd();
    }
    result += prot.readStructEnd();
    return result;
  }
  case T_MAP: {
    uint32_t result = 0;
    TType keyType;
    TType valType;
    uint32_t i, size;
    result += prot.readMapBegin(keyType, valType, size);
    for (i = 0; i < size; i++) {
      result += skip(prot, keyType);
      result += skip(prot, valType);
    }
    result += prot.readMapEnd();
    return result;
  }
  case T_SET: {
    uint32_t result = 0;
    TType elemType;
    uint32_t i, size;
    result += prot.readSetBegin(elemType, size);
    for (i = 0; i < size; i++) {
      result += skip(prot, elemType);
    }
    result += prot.readSetEnd();
    return result;
  }
  case T_LIST: {
    uint32_t result = 0;
    TType elemType;
    uint32_t i, size;
    result += prot.readListBegin(elemType, size);
    for (i = 0; i < size; i++) {
      result += skip(prot, elemType);
    }
    result += prot.readListEnd();
    return result;
  }
  case T_STOP:
  case T_VOID:
  case T_U64:
  case T_UTF8:
  case T_UTF16:
    break;
  }
  return 0;
}


f2fs: support swap file w/ DIO

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

static inline struct f2fs_sb_info *F2FS_P_SB(struct page *page)
{
	return F2FS_M_SB(page->mapping);
}

In the Linux kernel 5.0.21, mounting a crafted f2fs filesystem image can cause a NULL pointer dereference in f2fs_recover_fsync_data in fs/f2fs/recovery.c. This is related to F2FS_P_SB in fs/f2fs/f2fs.h.


static inline struct f2fs_sb_info *F2FS_P_SB(struct page *page)
{
	return F2FS_M_SB(page_file_mapping(page));
}


powerpc/mm/64s/hash: Reallocate context ids on fork

When using the Hash Page Table (HPT) MMU, userspace memory mappings
are managed at two levels. Firstly in the Linux page tables, much like
other architectures, and secondly in the SLB (Segment Lookaside
Buffer) and HPT. It's the SLB and HPT that are actually used by the
hardware to do translations.

As part of the series adding support for 4PB user virtual address
space using the hash MMU, we added support for allocating multiple
"context ids" per process, one for each 512TB chunk of address space.
These are tracked in an array called extended_id in the mm_context_t
of a process that has done a mapping above 512TB.

If such a process forks (ie. clone(2) without CLONE_VM set) it's mm is
copied, including the mm_context_t, and then init_new_context() is
called to reinitialise parts of the mm_context_t as appropriate to
separate the address spaces of the two processes.

The key step in ensuring the two processes have separate address
spaces is to allocate a new context id for the process, this is done
at the beginning of hash__init_new_context(). If we didn't allocate a
new context id then the two processes would share mappings as far as
the SLB and HPT are concerned, even though their Linux page tables
would be separate.

For mappings above 512TB, which use the extended_id array, we
neglected to allocate new context ids on fork, meaning the parent and
child use the same ids and therefore share those mappings even though
they're supposed to be separate. This can lead to the parent seeing
writes done by the child, which is essentially memory corruption.

There is an additional exposure which is that if the child process
exits, all its context ids are freed, including the context ids that
are still in use by the parent for mappings above 512TB. One or more
of those ids can then be reallocated to a third process, that process
can then read/write to the parent's mappings above 512TB. Additionally
if the freed id is used for the third process's primary context id,
then the parent is able to read/write to the third process's mappings
*below* 512TB.

All of these are fundamental failures to enforce separation between
processes. The only mitigating factor is that the bug only occurs if a
process creates mappings above 512TB, and most applications still do
not create such mappings.

Only machines using the hash page table MMU are affected, eg. PowerPC
970 (G5), PA6T, Power5/6/7/8/9. By default Power9 bare metal machines
(powernv) use the Radix MMU and are not affected, unless the machine
has been explicitly booted in HPT mode (using disable_radix on the
kernel command line). KVM guests on Power9 may be affected if the host
or guest is configured to use the HPT MMU. LPARs under PowerVM on
Power9 are affected as they always use the HPT MMU. Kernels built with
PAGE_SIZE=4K are not affected.

The fix is relatively simple, we need to reallocate context ids for
all extended mappings on fork.

Fixes: f384796c40dc ("powerpc/mm: Add support for handling > 512TB address in SLB miss")
Cc: stable@vger.kernel.org # v4.17+
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

static int hash__init_new_context(struct mm_struct *mm)
{
	int index;

	index = hash__alloc_context_id();
	if (index < 0)
		return index;

	/*
	 * The old code would re-promote on fork, we don't do that when using
	 * slices as it could cause problem promoting slices that have been
	 * forced down to 4K.
	 *
	 * For book3s we have MMU_NO_CONTEXT set to be ~0. Hence check
	 * explicitly against context.id == 0. This ensures that we properly
	 * initialize context slice details for newly allocated mm's (which will
	 * have id == 0) and don't alter context slice inherited via fork (which
	 * will have id != 0).
	 *
	 * We should not be calling init_new_context() on init_mm. Hence a
	 * check against 0 is OK.
	 */
	if (mm->context.id == 0)
		slice_init_new_context_exec(mm);

	subpage_prot_init_new_context(mm);

	pkey_mm_init(mm);
	return index;
}

arch/powerpc/mm/mmu_context_book3s64.c in the Linux kernel before 5.1.15 for powerpc has a bug where unrelated processes may be able to read/write to one another's virtual memory under certain conditions via an mmap above 512 TB. Only a subset of powerpc systems are affected.


static int hash__init_new_context(struct mm_struct *mm)
{
	int index;

	/*
	 * The old code would re-promote on fork, we don't do that when using
	 * slices as it could cause problem promoting slices that have been
	 * forced down to 4K.
	 *
	 * For book3s we have MMU_NO_CONTEXT set to be ~0. Hence check
	 * explicitly against context.id == 0. This ensures that we properly
	 * initialize context slice details for newly allocated mm's (which will
	 * have id == 0) and don't alter context slice inherited via fork (which
	 * will have id != 0).
	 *
	 * We should not be calling init_new_context() on init_mm. Hence a
	 * check against 0 is OK.
	 */
	if (mm->context.id == 0)
		slice_init_new_context_exec(mm);

	index = realloc_context_ids(&mm->context);
	if (index < 0)
		return index;

	subpage_prot_init_new_context(mm);

	pkey_mm_init(mm);
	return index;
}


tightvnc-filetransfer: fix heap use-after-free

One can only guess what the intended semantics were here, but as every
other rfbCloseClient() call in this file is followed by an immediate
return, let's assume this was forgotton in this case.

Anyway, don't forget to clean up to not leak memory.

Closes #241

HandleFileDownloadCancelRequest(rfbClientPtr cl, rfbTightClientPtr rtcp)
{
	int n = 0;
	char *reason = NULL;
	rfbClientToServerTightMsg msg;

	memset(&msg, 0, sizeof(rfbClientToServerTightMsg));

	if((n = rfbReadExact(cl, ((char *)&msg)+1, sz_rfbFileDownloadCancelMsg-1)) <= 0) {

		if (n < 0)
			rfbLog("File [%s]: Method [%s]: Error while reading "
					"FileDownloadCancelMsg\n", __FILE__, __FUNCTION__);

	    rfbCloseClient(cl);
	    return;
	}

	msg.fdc.reasonLen = Swap16IfLE(msg.fdc.reasonLen);

	if(msg.fdc.reasonLen == 0) {
		rfbLog("File [%s]: Method [%s]: reason length received is Zero\n",
				__FILE__, __FUNCTION__);
		return;
	}

	reason = (char*) calloc(msg.fdc.reasonLen + 1, sizeof(char));
	if(reason == NULL) {
		rfbLog("File [%s]: Method [%s]: Fatal Error: Memory alloc failed\n",
				__FILE__, __FUNCTION__);
		return;
	}

	if((n = rfbReadExact(cl, reason, msg.fdc.reasonLen)) <= 0) {

		if (n < 0)
			rfbLog("File [%s]: Method [%s]: Error while reading "
					"FileDownloadCancelMsg\n", __FILE__, __FUNCTION__);

	    rfbCloseClient(cl);
	}

	rfbLog("File [%s]: Method [%s]: File Download Cancel Request received:"
					" reason <%s>\n", __FILE__, __FUNCTION__, reason);

	pthread_mutex_lock(&fileDownloadMutex);
	CloseUndoneFileTransfer(cl, rtcp);
	pthread_mutex_unlock(&fileDownloadMutex);

	if(reason != NULL) {
		free(reason);
		reason = NULL;
	}

}

LibVNC before commit ca2a5ac02fbbadd0a21fabba779c1ea69173d10b contains heap use-after-free vulnerability in server code of file transfer extension that can result remote code execution.


HandleFileDownloadCancelRequest(rfbClientPtr cl, rfbTightClientPtr rtcp)
{
	int n = 0;
	char *reason = NULL;
	rfbClientToServerTightMsg msg;

	memset(&msg, 0, sizeof(rfbClientToServerTightMsg));

	if((n = rfbReadExact(cl, ((char *)&msg)+1, sz_rfbFileDownloadCancelMsg-1)) <= 0) {

		if (n < 0)
			rfbLog("File [%s]: Method [%s]: Error while reading "
					"FileDownloadCancelMsg\n", __FILE__, __FUNCTION__);

	    rfbCloseClient(cl);
	    return;
	}

	msg.fdc.reasonLen = Swap16IfLE(msg.fdc.reasonLen);

	if(msg.fdc.reasonLen == 0) {
		rfbLog("File [%s]: Method [%s]: reason length received is Zero\n",
				__FILE__, __FUNCTION__);
		return;
	}

	reason = (char*) calloc(msg.fdc.reasonLen + 1, sizeof(char));
	if(reason == NULL) {
		rfbLog("File [%s]: Method [%s]: Fatal Error: Memory alloc failed\n",
				__FILE__, __FUNCTION__);
		return;
	}

	if((n = rfbReadExact(cl, reason, msg.fdc.reasonLen)) <= 0) {

		if (n < 0)
			rfbLog("File [%s]: Method [%s]: Error while reading "
					"FileDownloadCancelMsg\n", __FILE__, __FUNCTION__);

	    rfbCloseClient(cl);
	    free(reason);
	    return;
	}

	rfbLog("File [%s]: Method [%s]: File Download Cancel Request received:"
					" reason <%s>\n", __FILE__, __FUNCTION__, reason);

	pthread_mutex_lock(&fileDownloadMutex);
	CloseUndoneFileTransfer(cl, rtcp);
	pthread_mutex_unlock(&fileDownloadMutex);

	if(reason != NULL) {
		free(reason);
		reason = NULL;
	}

}


prevent overflow in bytesPerDeepLineTable (#1152)

* prevent overflow in bytesPerDeepLineTable

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>

* restore zapped 'const' from ImfMisc

Signed-off-by: Peter Hillman <peterh@wetafx.co.nz>

bytesPerDeepLineTable (const Header &header,
                       int minY, int maxY,
                       const char* base,
                       int xStride,
                       int yStride,
                       vector<size_t> &bytesPerLine)
{
    const Box2i &dataWindow = header.dataWindow();
    const ChannelList &channels = header.channels();

    for (ChannelList::ConstIterator c = channels.begin();
         c != channels.end();
         ++c)
    {
        const int ySampling = abs(c.channel().ySampling);
        const int xSampling = abs(c.channel().xSampling);
        const int pixelSize = pixelTypeSize (c.channel().type);

        // Here we transform from the domain over all pixels into the domain
        // of actual samples.  We want to sample points in [minY, maxY] where
        // (y % ySampling) == 0.  However, doing this by rejecting samples
        // requires O(height*width) modulo computations, which were a
        // significant bottleneck in the previous implementation of this
        // function.  For the low, low price of 4 divisions per channel, we
        // can tighten the y & x ranges to the least and greatest roots of the
        // sampling function and then stride by the sampling rate.
        const int sampleMinY = roundToNextMultiple(minY, ySampling);
        const int sampleMaxY = roundToPrevMultiple(maxY, ySampling);
        const int sampleMinX = roundToNextMultiple(dataWindow.min.x, xSampling);
        const int sampleMaxX = roundToPrevMultiple(dataWindow.max.x, xSampling);

        for (int y = sampleMinY; y <= sampleMaxY; y+=ySampling)
        {
            int nBytes = 0;
            for (int x = sampleMinX; x <= sampleMaxX; x += xSampling)
            {
                nBytes += pixelSize *
                          sampleCount(base, xStride, yStride, x, y);
            }
            bytesPerLine[y - dataWindow.min.y] += nBytes;
        }
    }

    size_t maxBytesPerLine = 0;

    for (int y = minY; y <= maxY; ++y)
        if (maxBytesPerLine < bytesPerLine[y - dataWindow.min.y])
            maxBytesPerLine = bytesPerLine[y - dataWindow.min.y];

    return maxBytesPerLine;
}

An integer overflow could occur when OpenEXR processes a crafted file on systems where size_t < 64 bits. This could cause an invalid bytesPerLine and maxBytesPerLine value, which could lead to problems with application stability or lead to other attack paths.


bytesPerDeepLineTable (const Header &header,
                       int minY, int maxY,
                       const char* base,
                       int xStride,
                       int yStride,
                       vector<size_t> &bytesPerLine)
{
    const Box2i &dataWindow = header.dataWindow();
    const ChannelList &channels = header.channels();

    for (ChannelList::ConstIterator c = channels.begin();
         c != channels.end();
         ++c)
    {
        const int ySampling = abs(c.channel().ySampling);
        const int xSampling = abs(c.channel().xSampling);
        const uint64_t pixelSize = pixelTypeSize (c.channel().type);

        // Here we transform from the domain over all pixels into the domain
        // of actual samples.  We want to sample points in [minY, maxY] where
        // (y % ySampling) == 0.  However, doing this by rejecting samples
        // requires O(height*width) modulo computations, which were a
        // significant bottleneck in the previous implementation of this
        // function.  For the low, low price of 4 divisions per channel, we
        // can tighten the y & x ranges to the least and greatest roots of the
        // sampling function and then stride by the sampling rate.
        const int sampleMinY = roundToNextMultiple(minY, ySampling);
        const int sampleMaxY = roundToPrevMultiple(maxY, ySampling);
        const int sampleMinX = roundToNextMultiple(dataWindow.min.x, xSampling);
        const int sampleMaxX = roundToPrevMultiple(dataWindow.max.x, xSampling);

        for (int y = sampleMinY; y <= sampleMaxY; y+=ySampling)
        {
            uint64_t nBytes = 0;
            for (int x = sampleMinX; x <= sampleMaxX; x += xSampling)
            {
                nBytes += pixelSize *
                          static_cast<uint64_t>(sampleCount(base, xStride, yStride, x, y));
            }

            //
            // architectures where size_t is smaller than 64 bits may overflow
            // (scanlines with more than 2^32 bytes are not currently supported so this should not occur with valid files)
            //
            if( static_cast<uint64_t>(bytesPerLine[y - dataWindow.min.y]) + nBytes > SIZE_MAX)
            {
                throw IEX_NAMESPACE::IoExc("Scanline size too large");
            }

            bytesPerLine[y - dataWindow.min.y] += nBytes;
        }
    }

    size_t maxBytesPerLine = 0;

    for (int y = minY; y <= maxY; ++y)
    {
        if (maxBytesPerLine < bytesPerLine[y - dataWindow.min.y])
        {
            maxBytesPerLine = bytesPerLine[y - dataWindow.min.y];
        }
    }

    return maxBytesPerLine;
}


aacparse: Make sure we have enough data in the codec_data to be able to parse it

Also error out cleanly if mapping the buffer failed.

https://bugzilla.gnome.org/show_bug.cgi?id=775450

gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
{
  GstAacParse *aacparse;
  GstStructure *structure;
  gchar *caps_str;
  const GValue *value;

  aacparse = GST_AAC_PARSE (parse);
  structure = gst_caps_get_structure (caps, 0);
  caps_str = gst_caps_to_string (caps);

  GST_DEBUG_OBJECT (aacparse, "setcaps: %s", caps_str);
  g_free (caps_str);

  /* This is needed at least in case of RTP
   * Parses the codec_data information to get ObjectType,
   * number of channels and samplerate */
  value = gst_structure_get_value (structure, "codec_data");
  if (value) {
    GstBuffer *buf = gst_value_get_buffer (value);

    if (buf) {
      GstMapInfo map;
      guint sr_idx;

      gst_buffer_map (buf, &map, GST_MAP_READ);

      sr_idx = ((map.data[0] & 0x07) << 1) | ((map.data[1] & 0x80) >> 7);
      aacparse->object_type = (map.data[0] & 0xf8) >> 3;
      aacparse->sample_rate =
          gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
      aacparse->channels = (map.data[1] & 0x78) >> 3;
      if (aacparse->channels == 7)
        aacparse->channels = 8;
      else if (aacparse->channels == 11)
        aacparse->channels = 7;
      else if (aacparse->channels == 12 || aacparse->channels == 14)
        aacparse->channels = 8;
      aacparse->header_type = DSPAAC_HEADER_NONE;
      aacparse->mpegversion = 4;
      aacparse->frame_samples = (map.data[1] & 4) ? 960 : 1024;
      gst_buffer_unmap (buf, &map);

      GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d, "
          "samples=%d", aacparse->object_type, aacparse->sample_rate,
          aacparse->channels, aacparse->frame_samples);

      /* arrange for metadata and get out of the way */
      gst_aac_parse_set_src_caps (aacparse, caps);
      if (aacparse->header_type == aacparse->output_header_type)
        gst_base_parse_set_passthrough (parse, TRUE);
    } else {
      return FALSE;
    }

    /* caps info overrides */
    gst_structure_get_int (structure, "rate", &aacparse->sample_rate);
    gst_structure_get_int (structure, "channels", &aacparse->channels);
  } else {
    const gchar *stream_format =
        gst_structure_get_string (structure, "stream-format");

    if (g_strcmp0 (stream_format, "raw") == 0) {
      GST_ERROR_OBJECT (parse, "Need codec_data for raw AAC");
      return FALSE;
    } else {
      aacparse->sample_rate = 0;
      aacparse->channels = 0;
      aacparse->header_type = DSPAAC_HEADER_NOT_PARSED;
      gst_base_parse_set_passthrough (parse, FALSE);
    }
  }
  return TRUE;
}

The gst_aac_parse_sink_setcaps function in gst/audioparsers/gstaacparse.c in gst-plugins-good in GStreamer before 1.10.3 allows remote attackers to cause a denial of service (invalid memory read and crash) via a crafted audio file.


gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
{
  GstAacParse *aacparse;
  GstStructure *structure;
  gchar *caps_str;
  const GValue *value;

  aacparse = GST_AAC_PARSE (parse);
  structure = gst_caps_get_structure (caps, 0);
  caps_str = gst_caps_to_string (caps);

  GST_DEBUG_OBJECT (aacparse, "setcaps: %s", caps_str);
  g_free (caps_str);

  /* This is needed at least in case of RTP
   * Parses the codec_data information to get ObjectType,
   * number of channels and samplerate */
  value = gst_structure_get_value (structure, "codec_data");
  if (value) {
    GstBuffer *buf = gst_value_get_buffer (value);

    if (buf && gst_buffer_get_size (buf) >= 2) {
      GstMapInfo map;
      guint sr_idx;

      if (!gst_buffer_map (buf, &map, GST_MAP_READ))
        return FALSE;

      sr_idx = ((map.data[0] & 0x07) << 1) | ((map.data[1] & 0x80) >> 7);
      aacparse->object_type = (map.data[0] & 0xf8) >> 3;
      aacparse->sample_rate =
          gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
      aacparse->channels = (map.data[1] & 0x78) >> 3;
      if (aacparse->channels == 7)
        aacparse->channels = 8;
      else if (aacparse->channels == 11)
        aacparse->channels = 7;
      else if (aacparse->channels == 12 || aacparse->channels == 14)
        aacparse->channels = 8;
      aacparse->header_type = DSPAAC_HEADER_NONE;
      aacparse->mpegversion = 4;
      aacparse->frame_samples = (map.data[1] & 4) ? 960 : 1024;
      gst_buffer_unmap (buf, &map);

      GST_DEBUG ("codec_data: object_type=%d, sample_rate=%d, channels=%d, "
          "samples=%d", aacparse->object_type, aacparse->sample_rate,
          aacparse->channels, aacparse->frame_samples);

      /* arrange for metadata and get out of the way */
      gst_aac_parse_set_src_caps (aacparse, caps);
      if (aacparse->header_type == aacparse->output_header_type)
        gst_base_parse_set_passthrough (parse, TRUE);
    } else {
      return FALSE;
    }

    /* caps info overrides */
    gst_structure_get_int (structure, "rate", &aacparse->sample_rate);
    gst_structure_get_int (structure, "channels", &aacparse->channels);
  } else {
    const gchar *stream_format =
        gst_structure_get_string (structure, "stream-format");

    if (g_strcmp0 (stream_format, "raw") == 0) {
      GST_ERROR_OBJECT (parse, "Need codec_data for raw AAC");
      return FALSE;
    } else {
      aacparse->sample_rate = 0;
      aacparse->channels = 0;
      aacparse->header_type = DSPAAC_HEADER_NOT_PARSED;
      gst_base_parse_set_passthrough (parse, FALSE);
    }
  }
  return TRUE;
}


Handle empty Tight gradient rects

We always assumed there would be one pixel per row so a rect with
a zero width would result in us writing to unknown memory.

This could theoretically be used by a malicious server to inject
code in to the viewer process.

Issue found by Pavel Cheremushkin from Kaspersky Lab.

void TightDecoder::FilterGradient(const rdr::U8* inbuf,
                                  const PixelFormat& pf, PIXEL_T* outbuf,
                                  int stride, const Rect& r)
{
  int x, y, c;
  static rdr::U8 prevRow[TIGHT_MAX_WIDTH*3];
  static rdr::U8 thisRow[TIGHT_MAX_WIDTH*3];
  rdr::U8 pix[3];
  int est[3];

  memset(prevRow, 0, sizeof(prevRow));

  // Set up shortcut variables
  int rectHeight = r.height();
  int rectWidth = r.width();

  for (y = 0; y < rectHeight; y++) {
    /* First pixel in a row */
    pf.rgbFromBuffer(pix, &inbuf[y*rectWidth], 1);
    for (c = 0; c < 3; c++)
      pix[c] += prevRow[c];

    memcpy(thisRow, pix, sizeof(pix));

    pf.bufferFromRGB((rdr::U8*)&outbuf[y*stride], pix, 1);

    /* Remaining pixels of a row */
    for (x = 1; x < rectWidth; x++) {
      for (c = 0; c < 3; c++) {
        est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
        if (est[c] > 255) {
          est[c] = 255;
        } else if (est[c] < 0) {
          est[c] = 0;
        }
      }

      pf.rgbFromBuffer(pix, &inbuf[y*rectWidth+x], 1);
      for (c = 0; c < 3; c++)
        pix[c] += est[c];

      memcpy(&thisRow[x*3], pix, sizeof(pix));

      pf.bufferFromRGB((rdr::U8*)&outbuf[y*stride+x], pix, 1);
    }

    memcpy(prevRow, thisRow, sizeof(prevRow));
  }
}

TigerVNC version prior to 1.10.1 is vulnerable to heap buffer overflow, which occurs in TightDecoder::FilterGradient. Exploitation of this vulnerability could potentially result into remote code execution. This attack appear to be exploitable via network connectivity.


void TightDecoder::FilterGradient(const rdr::U8* inbuf,
                                  const PixelFormat& pf, PIXEL_T* outbuf,
                                  int stride, const Rect& r)
{
  int x, y, c;
  static rdr::U8 prevRow[TIGHT_MAX_WIDTH*3];
  static rdr::U8 thisRow[TIGHT_MAX_WIDTH*3];
  rdr::U8 pix[3];
  int est[3];

  memset(prevRow, 0, sizeof(prevRow));

  // Set up shortcut variables
  int rectHeight = r.height();
  int rectWidth = r.width();

  for (y = 0; y < rectHeight; y++) {
    for (x = 0; x < rectWidth; x++) {
      /* First pixel in a row */
      if (x == 0) {
        pf.rgbFromBuffer(pix, &inbuf[y*rectWidth], 1);
        for (c = 0; c < 3; c++)
          pix[c] += prevRow[c];

        memcpy(thisRow, pix, sizeof(pix));

        pf.bufferFromRGB((rdr::U8*)&outbuf[y*stride], pix, 1);

        continue;
      }

      for (c = 0; c < 3; c++) {
        est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
        if (est[c] > 255) {
          est[c] = 255;
        } else if (est[c] < 0) {
          est[c] = 0;
        }
      }

      pf.rgbFromBuffer(pix, &inbuf[y*rectWidth+x], 1);
      for (c = 0; c < 3; c++)
        pix[c] += est[c];

      memcpy(&thisRow[x*3], pix, sizeof(pix));

      pf.bufferFromRGB((rdr::U8*)&outbuf[y*stride+x], pix, 1);
    }

    memcpy(prevRow, thisRow, sizeof(prevRow));
  }
}


Memory error in Storage.c when accepting negative image size arguments

ImagingNew(const char* mode, int xsize, int ysize)
{
    int bytes;
    Imaging im;

    if (strlen(mode) == 1) {
        if (mode[0] == 'F' || mode[0] == 'I')
            bytes = 4;
        else
            bytes = 1;
    } else
        bytes = strlen(mode); /* close enough */

    if ((int64_t) xsize * (int64_t) ysize <= THRESHOLD / bytes) {
        im = ImagingNewBlock(mode, xsize, ysize);
        if (im)
            return im;
        /* assume memory error; try allocating in array mode instead */
        ImagingError_Clear();
    }

    return ImagingNewArray(mode, xsize, ysize);
}

Pillow before 3.3.2 allows context-dependent attackers to execute arbitrary code by using the "crafted image file" approach, related to an "Insecure Sign Extension" issue affecting the ImagingNew in Storage.c component.


ImagingNew(const char* mode, int xsize, int ysize)
{
    int bytes;
    Imaging im;

    if (strlen(mode) == 1) {
        if (mode[0] == 'F' || mode[0] == 'I')
            bytes = 4;
        else
            bytes = 1;
    } else
        bytes = strlen(mode); /* close enough */

    if (xsize < 0 || ysize < 0) {
        return (Imaging) ImagingError_ValueError("bad image size");
    }

    if ((int64_t) xsize * (int64_t) ysize <= THRESHOLD / bytes) {
        im = ImagingNewBlock(mode, xsize, ysize);
        if (im)
            return im;
        /* assume memory error; try allocating in array mode instead */
        ImagingError_Clear();
    }

    return ImagingNewArray(mode, xsize, ysize);
}


fix #164: Integer overflow related to reg->dmax in search_in_range()

search_in_range(regex_t* reg, const UChar* str, const UChar* end,
                const UChar* start, const UChar* range, /* match start range */
                const UChar* data_range, /* subject string range */
                OnigRegion* region,
                OnigOptionType option, OnigMatchParam* mp)
{
  int r;
  UChar *s, *prev;
  MatchArg msa;
  const UChar *orig_start = start;

#ifdef ONIG_DEBUG_SEARCH
  fprintf(stderr,
     "onig_search (entry point): str: %p, end: %d, start: %d, range: %d\n",
     str, (int )(end - str), (int )(start - str), (int )(range - str));
#endif

  ADJUST_MATCH_PARAM(reg, mp);

  if (region
#ifdef USE_POSIX_API_REGION_OPTION
      && !IS_POSIX_REGION(option)
#endif
      ) {
    r = onig_region_resize_clear(region, reg->num_mem + 1);
    if (r != 0) goto finish_no_msa;
  }

  if (start > end || start < str) goto mismatch_no_msa;

  if (ONIG_IS_OPTION_ON(option, ONIG_OPTION_CHECK_VALIDITY_OF_STRING)) {
    if (! ONIGENC_IS_VALID_MBC_STRING(reg->enc, str, end)) {
      r = ONIGERR_INVALID_WIDE_CHAR_VALUE;
      goto finish_no_msa;
    }
  }

#ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
#define MATCH_AND_RETURN_CHECK(upper_range) \
  r = match_at(reg, str, end, (upper_range), s, prev, &msa); \
  if (r != ONIG_MISMATCH) {\
    if (r >= 0) {\
      if (! IS_FIND_LONGEST(reg->options)) {\
        goto match;\
      }\
    }\
    else goto finish; /* error */ \
  }
#else
#define MATCH_AND_RETURN_CHECK(upper_range) \
  r = match_at(reg, str, end, (upper_range), s, prev, &msa); \
  if (r != ONIG_MISMATCH) {\
    if (r >= 0) {\
      goto match;\
    }\
    else goto finish; /* error */ \
  }
#endif /* USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE */

  /* anchor optimize: resume search range */
  if (reg->anchor != 0 && str < end) {
    UChar *min_semi_end, *max_semi_end;

    if (reg->anchor & ANCR_BEGIN_POSITION) {
      /* search start-position only */
    begin_position:
      if (range > start)
        range = start + 1;
      else
        range = start;
    }
    else if (reg->anchor & ANCR_BEGIN_BUF) {
      /* search str-position only */
      if (range > start) {
        if (start != str) goto mismatch_no_msa;
        range = str + 1;
      }
      else {
        if (range <= str) {
          start = str;
          range = str;
        }
        else
          goto mismatch_no_msa;
      }
    }
    else if (reg->anchor & ANCR_END_BUF) {
      min_semi_end = max_semi_end = (UChar* )end;

    end_buf:
      if ((OnigLen )(max_semi_end - str) < reg->anc_dist_min)
        goto mismatch_no_msa;

      if (range > start) {
        if ((OnigLen )(min_semi_end - start) > reg->anc_dist_max) {
          start = min_semi_end - reg->anc_dist_max;
          if (start < end)
            start = onigenc_get_right_adjust_char_head(reg->enc, str, start);
        }
        if ((OnigLen )(max_semi_end - (range - 1)) < reg->anc_dist_min) {
          range = max_semi_end - reg->anc_dist_min + 1;
        }

        if (start > range) goto mismatch_no_msa;
        /* If start == range, match with empty at end.
           Backward search is used. */
      }
      else {
        if ((OnigLen )(min_semi_end - range) > reg->anc_dist_max) {
          range = min_semi_end - reg->anc_dist_max;
        }
        if ((OnigLen )(max_semi_end - start) < reg->anc_dist_min) {
          start = max_semi_end - reg->anc_dist_min;
          start = ONIGENC_LEFT_ADJUST_CHAR_HEAD(reg->enc, str, start);
        }
        if (range > start) goto mismatch_no_msa;
      }
    }
    else if (reg->anchor & ANCR_SEMI_END_BUF) {
      UChar* pre_end = ONIGENC_STEP_BACK(reg->enc, str, end, 1);

      max_semi_end = (UChar* )end;
      if (ONIGENC_IS_MBC_NEWLINE(reg->enc, pre_end, end)) {
        min_semi_end = pre_end;

#ifdef USE_CRNL_AS_LINE_TERMINATOR
        pre_end = ONIGENC_STEP_BACK(reg->enc, str, pre_end, 1);
        if (IS_NOT_NULL(pre_end) &&
            ONIGENC_IS_MBC_CRNL(reg->enc, pre_end, end)) {
          min_semi_end = pre_end;
        }
#endif
        if (min_semi_end > str && start <= min_semi_end) {
          goto end_buf;
        }
      }
      else {
        min_semi_end = (UChar* )end;
        goto end_buf;
      }
    }
    else if ((reg->anchor & ANCR_ANYCHAR_INF_ML)) {
      goto begin_position;
    }
  }
  else if (str == end) { /* empty string */
    static const UChar* address_for_empty_string = (UChar* )"";

#ifdef ONIG_DEBUG_SEARCH
    fprintf(stderr, "onig_search: empty string.\n");
#endif

    if (reg->threshold_len == 0) {
      start = end = str = address_for_empty_string;
      s = (UChar* )start;
      prev = (UChar* )NULL;

      MATCH_ARG_INIT(msa, reg, option, region, start, mp);
      MATCH_AND_RETURN_CHECK(end);
      goto mismatch;
    }
    goto mismatch_no_msa;
  }

#ifdef ONIG_DEBUG_SEARCH
  fprintf(stderr, "onig_search(apply anchor): end: %d, start: %d, range: %d\n",
          (int )(end - str), (int )(start - str), (int )(range - str));
#endif

  MATCH_ARG_INIT(msa, reg, option, region, orig_start, mp);

  s = (UChar* )start;
  if (range > start) {   /* forward search */
    if (s > str)
      prev = onigenc_get_prev_char_head(reg->enc, str, s);
    else
      prev = (UChar* )NULL;

    if (reg->optimize != OPTIMIZE_NONE) {
      UChar *sch_range, *low, *high, *low_prev;

      if (reg->dist_max != 0) {
        if (reg->dist_max == INFINITE_LEN)
          sch_range = (UChar* )end;
        else {
          if ((end - range) < reg->dist_max)
            sch_range = (UChar* )end;
          else {
            sch_range = (UChar* )range + reg->dist_max;
          }
        }
      }
      else
        sch_range = (UChar* )range;

      if ((end - start) < reg->threshold_len)
        goto mismatch;

      if (reg->dist_max != INFINITE_LEN) {
        do {
          if (! forward_search(reg, str, end, s, sch_range, &low, &high,
                               &low_prev)) goto mismatch;
          if (s < low) {
            s    = low;
            prev = low_prev;
          }
          while (s <= high) {
            MATCH_AND_RETURN_CHECK(data_range);
            prev = s;
            s += enclen(reg->enc, s);
          }
        } while (s < range);
        goto mismatch;
      }
      else { /* check only. */
        if (! forward_search(reg, str, end, s, sch_range, &low, &high,
                             (UChar** )NULL)) goto mismatch;

        if ((reg->anchor & ANCR_ANYCHAR_INF) != 0) {
          do {
            MATCH_AND_RETURN_CHECK(data_range);
            prev = s;
            s += enclen(reg->enc, s);

            if ((reg->anchor & (ANCR_LOOK_BEHIND | ANCR_PREC_READ_NOT)) == 0) {
              while (!ONIGENC_IS_MBC_NEWLINE(reg->enc, prev, end) && s < range) {
                prev = s;
                s += enclen(reg->enc, s);
              }
            }
          } while (s < range);
          goto mismatch;
        }
      }
    }

    do {
      MATCH_AND_RETURN_CHECK(data_range);
      prev = s;
      s += enclen(reg->enc, s);
    } while (s < range);

    if (s == range) { /* because empty match with /$/. */
      MATCH_AND_RETURN_CHECK(data_range);
    }
  }
  else {  /* backward search */
    if (range < str) goto mismatch;

    if (orig_start < end)
      orig_start += enclen(reg->enc, orig_start); /* is upper range */

    if (reg->optimize != OPTIMIZE_NONE) {
      UChar *low, *high, *adjrange, *sch_start;
      const UChar *min_range;

      if ((end - range) < reg->threshold_len) goto mismatch;

      if (range < end)
        adjrange = ONIGENC_LEFT_ADJUST_CHAR_HEAD(reg->enc, str, range);
      else
        adjrange = (UChar* )end;

      if (end - range > reg->dist_min)
        min_range = range + reg->dist_min;
      else
        min_range = end;

      if (reg->dist_max != INFINITE_LEN) {
        do {
          if (end - s > reg->dist_max)
            sch_start = s + reg->dist_max;
          else {
            sch_start = onigenc_get_prev_char_head(reg->enc, str, end);
          }

          if (backward_search(reg, str, end, sch_start, min_range, adjrange,
                              &low, &high) <= 0)
            goto mismatch;

          if (s > high)
            s = high;

          while (s >= low) {
            prev = onigenc_get_prev_char_head(reg->enc, str, s);
            MATCH_AND_RETURN_CHECK(orig_start);
            s = prev;
          }
        } while (s >= range);
        goto mismatch;
      }
      else { /* check only. */
        sch_start = onigenc_get_prev_char_head(reg->enc, str, end);

        if (backward_search(reg, str, end, sch_start, min_range, adjrange,
                            &low, &high) <= 0) goto mismatch;
      }
    }

    do {
      prev = onigenc_get_prev_char_head(reg->enc, str, s);
      MATCH_AND_RETURN_CHECK(orig_start);
      s = prev;
    } while (s >= range);
  }

 mismatch:
#ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
  if (IS_FIND_LONGEST(reg->options)) {
    if (msa.best_len >= 0) {
      s = msa.best_s;
      goto match;
    }
  }
#endif
  r = ONIG_MISMATCH;

 finish:
  MATCH_ARG_FREE(msa);

  /* If result is mismatch and no FIND_NOT_EMPTY option,
     then the region is not set in match_at(). */
  if (IS_FIND_NOT_EMPTY(reg->options) && region
#ifdef USE_POSIX_API_REGION_OPTION
      && !IS_POSIX_REGION(option)
#endif
      ) {
    onig_region_clear(region);
  }

#ifdef ONIG_DEBUG
  if (r != ONIG_MISMATCH)
    fprintf(stderr, "onig_search: error %d\n", r);
#endif
  return r;

 mismatch_no_msa:
  r = ONIG_MISMATCH;
 finish_no_msa:
#ifdef ONIG_DEBUG
  if (r != ONIG_MISMATCH)
    fprintf(stderr, "onig_search: error %d\n", r);
#endif
  return r;

 match:
  MATCH_ARG_FREE(msa);
  return (int )(s - str);
}

An integer overflow in the search_in_range function in regexec.c in Oniguruma 6.x before 6.9.4_rc2 leads to an out-of-bounds read, in which the offset of this read is under the control of an attacker. (This only affects the 32-bit compiled version). Remote attackers can cause a denial-of-service or information disclosure, or possibly have unspecified other impact, via a crafted regular expression.


search_in_range(regex_t* reg, const UChar* str, const UChar* end,
                const UChar* start, const UChar* range, /* match start range */
                const UChar* data_range, /* subject string range */
                OnigRegion* region,
                OnigOptionType option, OnigMatchParam* mp)
{
  int r;
  UChar *s, *prev;
  MatchArg msa;
  const UChar *orig_start = start;

#ifdef ONIG_DEBUG_SEARCH
  fprintf(stderr,
     "onig_search (entry point): str: %p, end: %d, start: %d, range: %d\n",
     str, (int )(end - str), (int )(start - str), (int )(range - str));
#endif

  ADJUST_MATCH_PARAM(reg, mp);

  if (region
#ifdef USE_POSIX_API_REGION_OPTION
      && !IS_POSIX_REGION(option)
#endif
      ) {
    r = onig_region_resize_clear(region, reg->num_mem + 1);
    if (r != 0) goto finish_no_msa;
  }

  if (start > end || start < str) goto mismatch_no_msa;

  if (ONIG_IS_OPTION_ON(option, ONIG_OPTION_CHECK_VALIDITY_OF_STRING)) {
    if (! ONIGENC_IS_VALID_MBC_STRING(reg->enc, str, end)) {
      r = ONIGERR_INVALID_WIDE_CHAR_VALUE;
      goto finish_no_msa;
    }
  }

#ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
#define MATCH_AND_RETURN_CHECK(upper_range) \
  r = match_at(reg, str, end, (upper_range), s, prev, &msa); \
  if (r != ONIG_MISMATCH) {\
    if (r >= 0) {\
      if (! IS_FIND_LONGEST(reg->options)) {\
        goto match;\
      }\
    }\
    else goto finish; /* error */ \
  }
#else
#define MATCH_AND_RETURN_CHECK(upper_range) \
  r = match_at(reg, str, end, (upper_range), s, prev, &msa); \
  if (r != ONIG_MISMATCH) {\
    if (r >= 0) {\
      goto match;\
    }\
    else goto finish; /* error */ \
  }
#endif /* USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE */

  /* anchor optimize: resume search range */
  if (reg->anchor != 0 && str < end) {
    UChar *min_semi_end, *max_semi_end;

    if (reg->anchor & ANCR_BEGIN_POSITION) {
      /* search start-position only */
    begin_position:
      if (range > start)
        range = start + 1;
      else
        range = start;
    }
    else if (reg->anchor & ANCR_BEGIN_BUF) {
      /* search str-position only */
      if (range > start) {
        if (start != str) goto mismatch_no_msa;
        range = str + 1;
      }
      else {
        if (range <= str) {
          start = str;
          range = str;
        }
        else
          goto mismatch_no_msa;
      }
    }
    else if (reg->anchor & ANCR_END_BUF) {
      min_semi_end = max_semi_end = (UChar* )end;

    end_buf:
      if ((OnigLen )(max_semi_end - str) < reg->anc_dist_min)
        goto mismatch_no_msa;

      if (range > start) {
        if (min_semi_end - start > reg->anc_dist_max) {
          start = min_semi_end - reg->anc_dist_max;
          if (start < end)
            start = onigenc_get_right_adjust_char_head(reg->enc, str, start);
        }
        if (max_semi_end - (range - 1) < reg->anc_dist_min) {
          if (max_semi_end - str + 1 < reg->anc_dist_min)
            goto mismatch_no_msa;
          else
            range = max_semi_end - reg->anc_dist_min + 1;
        }

        if (start > range) goto mismatch_no_msa;
        /* If start == range, match with empty at end.
           Backward search is used. */
      }
      else {
        if (min_semi_end - range > reg->anc_dist_max) {
          range = min_semi_end - reg->anc_dist_max;
        }
        if (max_semi_end - start < reg->anc_dist_min) {
          if (max_semi_end - str < reg->anc_dist_min)
            goto mismatch_no_msa;
          else {
            start = max_semi_end - reg->anc_dist_min;
            start = ONIGENC_LEFT_ADJUST_CHAR_HEAD(reg->enc, str, start);
          }
        }
        if (range > start) goto mismatch_no_msa;
      }
    }
    else if (reg->anchor & ANCR_SEMI_END_BUF) {
      UChar* pre_end = ONIGENC_STEP_BACK(reg->enc, str, end, 1);

      max_semi_end = (UChar* )end;
      if (ONIGENC_IS_MBC_NEWLINE(reg->enc, pre_end, end)) {
        min_semi_end = pre_end;

#ifdef USE_CRNL_AS_LINE_TERMINATOR
        pre_end = ONIGENC_STEP_BACK(reg->enc, str, pre_end, 1);
        if (IS_NOT_NULL(pre_end) &&
            ONIGENC_IS_MBC_CRNL(reg->enc, pre_end, end)) {
          min_semi_end = pre_end;
        }
#endif
        if (min_semi_end > str && start <= min_semi_end) {
          goto end_buf;
        }
      }
      else {
        min_semi_end = (UChar* )end;
        goto end_buf;
      }
    }
    else if ((reg->anchor & ANCR_ANYCHAR_INF_ML)) {
      goto begin_position;
    }
  }
  else if (str == end) { /* empty string */
    static const UChar* address_for_empty_string = (UChar* )"";

#ifdef ONIG_DEBUG_SEARCH
    fprintf(stderr, "onig_search: empty string.\n");
#endif

    if (reg->threshold_len == 0) {
      start = end = str = address_for_empty_string;
      s = (UChar* )start;
      prev = (UChar* )NULL;

      MATCH_ARG_INIT(msa, reg, option, region, start, mp);
      MATCH_AND_RETURN_CHECK(end);
      goto mismatch;
    }
    goto mismatch_no_msa;
  }

#ifdef ONIG_DEBUG_SEARCH
  fprintf(stderr, "onig_search(apply anchor): end: %d, start: %d, range: %d\n",
          (int )(end - str), (int )(start - str), (int )(range - str));
#endif

  MATCH_ARG_INIT(msa, reg, option, region, orig_start, mp);

  s = (UChar* )start;
  if (range > start) {   /* forward search */
    if (s > str)
      prev = onigenc_get_prev_char_head(reg->enc, str, s);
    else
      prev = (UChar* )NULL;

    if (reg->optimize != OPTIMIZE_NONE) {
      UChar *sch_range, *low, *high, *low_prev;

      if (reg->dist_max != 0) {
        if (reg->dist_max == INFINITE_LEN)
          sch_range = (UChar* )end;
        else {
          if ((end - range) < reg->dist_max)
            sch_range = (UChar* )end;
          else {
            sch_range = (UChar* )range + reg->dist_max;
          }
        }
      }
      else
        sch_range = (UChar* )range;

      if ((end - start) < reg->threshold_len)
        goto mismatch;

      if (reg->dist_max != INFINITE_LEN) {
        do {
          if (! forward_search(reg, str, end, s, sch_range, &low, &high,
                               &low_prev)) goto mismatch;
          if (s < low) {
            s    = low;
            prev = low_prev;
          }
          while (s <= high) {
            MATCH_AND_RETURN_CHECK(data_range);
            prev = s;
            s += enclen(reg->enc, s);
          }
        } while (s < range);
        goto mismatch;
      }
      else { /* check only. */
        if (! forward_search(reg, str, end, s, sch_range, &low, &high,
                             (UChar** )NULL)) goto mismatch;

        if ((reg->anchor & ANCR_ANYCHAR_INF) != 0) {
          do {
            MATCH_AND_RETURN_CHECK(data_range);
            prev = s;
            s += enclen(reg->enc, s);

            if ((reg->anchor & (ANCR_LOOK_BEHIND | ANCR_PREC_READ_NOT)) == 0) {
              while (!ONIGENC_IS_MBC_NEWLINE(reg->enc, prev, end) && s < range) {
                prev = s;
                s += enclen(reg->enc, s);
              }
            }
          } while (s < range);
          goto mismatch;
        }
      }
    }

    do {
      MATCH_AND_RETURN_CHECK(data_range);
      prev = s;
      s += enclen(reg->enc, s);
    } while (s < range);

    if (s == range) { /* because empty match with /$/. */
      MATCH_AND_RETURN_CHECK(data_range);
    }
  }
  else {  /* backward search */
    if (range < str) goto mismatch;

    if (orig_start < end)
      orig_start += enclen(reg->enc, orig_start); /* is upper range */

    if (reg->optimize != OPTIMIZE_NONE) {
      UChar *low, *high, *adjrange, *sch_start;
      const UChar *min_range;

      if ((end - range) < reg->threshold_len) goto mismatch;

      if (range < end)
        adjrange = ONIGENC_LEFT_ADJUST_CHAR_HEAD(reg->enc, str, range);
      else
        adjrange = (UChar* )end;

      if (end - range > reg->dist_min)
        min_range = range + reg->dist_min;
      else
        min_range = end;

      if (reg->dist_max != INFINITE_LEN) {
        do {
          if (end - s > reg->dist_max)
            sch_start = s + reg->dist_max;
          else {
            sch_start = onigenc_get_prev_char_head(reg->enc, str, end);
          }

          if (backward_search(reg, str, end, sch_start, min_range, adjrange,
                              &low, &high) <= 0)
            goto mismatch;

          if (s > high)
            s = high;

          while (s >= low) {
            prev = onigenc_get_prev_char_head(reg->enc, str, s);
            MATCH_AND_RETURN_CHECK(orig_start);
            s = prev;
          }
        } while (s >= range);
        goto mismatch;
      }
      else { /* check only. */
        sch_start = onigenc_get_prev_char_head(reg->enc, str, end);

        if (backward_search(reg, str, end, sch_start, min_range, adjrange,
                            &low, &high) <= 0) goto mismatch;
      }
    }

    do {
      prev = onigenc_get_prev_char_head(reg->enc, str, s);
      MATCH_AND_RETURN_CHECK(orig_start);
      s = prev;
    } while (s >= range);
  }

 mismatch:
#ifdef USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
  if (IS_FIND_LONGEST(reg->options)) {
    if (msa.best_len >= 0) {
      s = msa.best_s;
      goto match;
    }
  }
#endif
  r = ONIG_MISMATCH;

 finish:
  MATCH_ARG_FREE(msa);

  /* If result is mismatch and no FIND_NOT_EMPTY option,
     then the region is not set in match_at(). */
  if (IS_FIND_NOT_EMPTY(reg->options) && region
#ifdef USE_POSIX_API_REGION_OPTION
      && !IS_POSIX_REGION(option)
#endif
      ) {
    onig_region_clear(region);
  }

#ifdef ONIG_DEBUG
  if (r != ONIG_MISMATCH)
    fprintf(stderr, "onig_search: error %d\n", r);
#endif
  return r;

 mismatch_no_msa:
  r = ONIG_MISMATCH;
 finish_no_msa:
#ifdef ONIG_DEBUG
  if (r != ONIG_MISMATCH)
    fprintf(stderr, "onig_search: error %d\n", r);
#endif
  return r;

 match:
  MATCH_ARG_FREE(msa);
  return (int )(s - str);
}


  bool Messageheader::Parser::state_fieldbody_crlf(char ch)
  {
    if (ch == '\r')
      SET_STATE(state_end_cr);
    else if (ch == '\n')
    {
      log_debug("header " << fieldnamePtr << ": " << fieldbodyPtr);
      switch (header.onField(fieldnamePtr, fieldbodyPtr))
      {
        case OK:
        case END:  return true;
                   break;
        case FAIL: failedFlag = true;
                   log_warn("invalid character " << chartoprint(ch) << " in fieldbody");
                   break;
      }

      *headerdataPtr = '\0';
      return true;
    }
    else if (std::isspace(ch))
    {
      // continuation line
      checkHeaderspace(1);
      *(headerdataPtr - 1) = '\n';
      *headerdataPtr++ = ch;
      SET_STATE(state_fieldbody);
    }
    else if (ch >= 33 && ch <= 126)
    {
      switch (header.onField(fieldnamePtr, fieldbodyPtr))
      {
        case OK:   SET_STATE(state_fieldname);
                   break;
        case FAIL: failedFlag = true;
                   log_warn("invalid character " << chartoprint(ch) << " in fieldbody");
                   break;
        case END:  return true;
                   break;
      }

      fieldnamePtr = headerdataPtr;
      checkHeaderspace(1);
      *headerdataPtr++ = ch;
    }
    return false;
  }

framework/common/messageheaderparser.cpp in Tntnet before 2.2.1 allows remote attackers to obtain sensitive information via a header that ends in \n instead of \r\n, which prevents a null terminator from being added and causes Tntnet to include headers from other requests.


  bool Messageheader::Parser::state_fieldbody_crlf(char ch)
  {
    if (ch == '\r')
      SET_STATE(state_end_cr);
    else if (ch == '\n')
    {
      log_debug("header " << fieldnamePtr << ": " << fieldbodyPtr);
      if (header.onField(fieldnamePtr, fieldbodyPtr) == FAIL)
      {
        failedFlag = true;
        log_warn("invalid character " << chartoprint(ch) << " in fieldbody");
      }

      *headerdataPtr = '\0';
      return true;
    }
    else if (std::isspace(ch))
    {
      // continuation line
      checkHeaderspace(1);
      *(headerdataPtr - 1) = '\n';
      *headerdataPtr++ = ch;
      SET_STATE(state_fieldbody);
    }
    else if (ch >= 33 && ch <= 126)
    {
      switch (header.onField(fieldnamePtr, fieldbodyPtr))
      {
        case OK:   SET_STATE(state_fieldname);
                   break;
        case FAIL: failedFlag = true;
                   log_warn("invalid character " << chartoprint(ch) << " in fieldbody");
                   break;
        case END:  return true;
                   break;
      }

      fieldnamePtr = headerdataPtr;
      checkHeaderspace(1);
      *headerdataPtr++ = ch;
    }
    return false;
  }


Prevent segfault when iso2022 parsing

Bug-Debian: https://github.com/tats/w3m/issues/14

wc_push_to_iso2022(Str os, wc_wchar_t cc, wc_status *st)
{
    wc_uchar g = 0;
    wc_bool is_wide = WC_FALSE, retry = WC_FALSE;
    wc_wchar_t cc2;

  while (1) {
    switch (WC_CCS_TYPE(cc.ccs)) {
    case WC_CCS_A_CS94:
	if (cc.ccs == WC_CCS_US_ASCII)
	    cc.ccs = st->g0_ccs;
	g = cs94_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_CS94W:
	is_wide = 1;
	switch (cc.ccs) {
#ifdef USE_UNICODE
	case WC_CCS_JIS_X_0212:
	    if (!WcOption.use_jisx0212 && WcOption.use_jisx0213 &&
		WcOption.ucs_conv) {
		cc2 = wc_jisx0212_to_jisx0213(cc);
		if (cc2.ccs == WC_CCS_JIS_X_0213_1 ||
		    cc2.ccs == WC_CCS_JIS_X_0213_2) {
		    cc = cc2;
		    continue;
		}
	    }
	    break;
	case WC_CCS_JIS_X_0213_1:
	case WC_CCS_JIS_X_0213_2:
	    if (!WcOption.use_jisx0213 && WcOption.use_jisx0212 &&
		WcOption.ucs_conv) {
		cc2 = wc_jisx0213_to_jisx0212(cc);
		if (cc2.ccs == WC_CCS_JIS_X_0212) {
		    cc = cc2;
		    continue;
		}
	    }
	    break;
#endif
	}
	g = cs94w_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_CS96:
	g = cs96_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_CS96W:
	is_wide = 1;
	g = cs96w_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_CS942:
	g = cs942_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_UNKNOWN_W:
	if (WcOption.no_replace)
	    return;
	is_wide = 1;
	cc.ccs = WC_CCS_US_ASCII;
	g = cs94_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	cc.code = ((wc_uint32)WC_REPLACE_W[0] << 8) | WC_REPLACE_W[1];
	break;
    case WC_CCS_A_UNKNOWN:
	if (WcOption.no_replace)
	    return;
	cc.ccs = WC_CCS_US_ASCII;
	g = cs94_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	cc.code = (wc_uint32)WC_REPLACE[0];
	break;
    default:
	if ((cc.ccs == WC_CCS_JOHAB || cc.ccs == WC_CCS_JOHAB_1 ||
		cc.ccs == WC_CCS_JOHAB_2 || cc.ccs == WC_CCS_JOHAB_3) &&
		cs94w_gmap[WC_F_KS_X_1001 - WC_F_ISO_BASE]) {
	    wc_wchar_t cc2 = wc_johab_to_ksx1001(cc);
	    if (cc2.ccs == WC_CCS_KS_X_1001) {
		cc = cc2;
		continue;
	    }
	}
#ifdef USE_UNICODE
	if (WcOption.ucs_conv)
	    cc = wc_any_to_iso2022(cc, st);
	else
#endif
	    cc.ccs = WC_CCS_IS_WIDE(cc.ccs) ? WC_CCS_UNKNOWN_W : WC_CCS_UNKNOWN;
	continue;
    }
    if (! g) {
#ifdef USE_UNICODE
	if (WcOption.ucs_conv && ! retry)
	    cc = wc_any_to_any_ces(cc, st);
	else
#endif
	    cc.ccs = WC_CCS_IS_WIDE(cc.ccs) ? WC_CCS_UNKNOWN_W : WC_CCS_UNKNOWN;
	retry = WC_TRUE;
	continue;
    }

    wc_push_iso2022_esc(os, cc.ccs, g, 1, st);
    if (is_wide)
	Strcat_char(os, (char)((cc.code >> 8) & 0x7f));
    Strcat_char(os, (char)(cc.code & 0x7f));
    return;
  }
}

An issue was discovered in the Tatsuya Kinoshita w3m fork before 0.5.3-31. w3m allows remote attackers to cause a denial of service (out-of-bounds array access) via a crafted HTML page.


wc_push_to_iso2022(Str os, wc_wchar_t cc, wc_status *st)
{
    wc_uchar g = 0;
    wc_bool is_wide = WC_FALSE, retry = WC_FALSE;
    wc_wchar_t cc2;

  while (1) {
    switch (WC_CCS_TYPE(cc.ccs)) {
    case WC_CCS_A_CS94:
	if (cc.ccs == WC_CCS_US_ASCII)
	    cc.ccs = st->g0_ccs;
	if (WC_CCS_INDEX(cc.ccs) >= WC_F_ISO_BASE)
	    g = cs94_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_CS94W:
	is_wide = 1;
	switch (cc.ccs) {
#ifdef USE_UNICODE
	case WC_CCS_JIS_X_0212:
	    if (!WcOption.use_jisx0212 && WcOption.use_jisx0213 &&
		WcOption.ucs_conv) {
		cc2 = wc_jisx0212_to_jisx0213(cc);
		if (cc2.ccs == WC_CCS_JIS_X_0213_1 ||
		    cc2.ccs == WC_CCS_JIS_X_0213_2) {
		    cc = cc2;
		    continue;
		}
	    }
	    break;
	case WC_CCS_JIS_X_0213_1:
	case WC_CCS_JIS_X_0213_2:
	    if (!WcOption.use_jisx0213 && WcOption.use_jisx0212 &&
		WcOption.ucs_conv) {
		cc2 = wc_jisx0213_to_jisx0212(cc);
		if (cc2.ccs == WC_CCS_JIS_X_0212) {
		    cc = cc2;
		    continue;
		}
	    }
	    break;
#endif
	}
	if (WC_CCS_INDEX(cc.ccs) >= WC_F_ISO_BASE)
	    g = cs94w_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_CS96:
	if (WC_CCS_INDEX(cc.ccs) >= WC_F_ISO_BASE)
	    g = cs96_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_CS96W:
	is_wide = 1;
	if (WC_CCS_INDEX(cc.ccs) >= WC_F_ISO_BASE)
	    g = cs96w_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_CS942:
	if (WC_CCS_INDEX(cc.ccs) >= WC_F_ISO_BASE)
	    g = cs942_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	break;
    case WC_CCS_A_UNKNOWN_W:
	if (WcOption.no_replace)
	    return;
	is_wide = 1;
	cc.ccs = WC_CCS_US_ASCII;
	if (WC_CCS_INDEX(cc.ccs) >= WC_F_ISO_BASE)
	    g = cs94_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	cc.code = ((wc_uint32)WC_REPLACE_W[0] << 8) | WC_REPLACE_W[1];
	break;
    case WC_CCS_A_UNKNOWN:
	if (WcOption.no_replace)
	    return;
	cc.ccs = WC_CCS_US_ASCII;
	if (WC_CCS_INDEX(cc.ccs) >= WC_F_ISO_BASE)
	    g = cs94_gmap[WC_CCS_INDEX(cc.ccs) - WC_F_ISO_BASE];
	cc.code = (wc_uint32)WC_REPLACE[0];
	break;
    default:
	if ((cc.ccs == WC_CCS_JOHAB || cc.ccs == WC_CCS_JOHAB_1 ||
		cc.ccs == WC_CCS_JOHAB_2 || cc.ccs == WC_CCS_JOHAB_3) &&
		cs94w_gmap[WC_F_KS_X_1001 - WC_F_ISO_BASE]) {
	    wc_wchar_t cc2 = wc_johab_to_ksx1001(cc);
	    if (cc2.ccs == WC_CCS_KS_X_1001) {
		cc = cc2;
		continue;
	    }
	}
#ifdef USE_UNICODE
	if (WcOption.ucs_conv)
	    cc = wc_any_to_iso2022(cc, st);
	else
#endif
	    cc.ccs = WC_CCS_IS_WIDE(cc.ccs) ? WC_CCS_UNKNOWN_W : WC_CCS_UNKNOWN;
	continue;
    }
    if (! g) {
#ifdef USE_UNICODE
	if (WcOption.ucs_conv && ! retry)
	    cc = wc_any_to_any_ces(cc, st);
	else
#endif
	    cc.ccs = WC_CCS_IS_WIDE(cc.ccs) ? WC_CCS_UNKNOWN_W : WC_CCS_UNKNOWN;
	retry = WC_TRUE;
	continue;
    }

    wc_push_iso2022_esc(os, cc.ccs, g, 1, st);
    if (is_wide)
	Strcat_char(os, (char)((cc.code >> 8) & 0x7f));
    Strcat_char(os, (char)(cc.code & 0x7f));
    return;
  }
}


PSD: enforce Length of image resource section < file size

    void PsdImage::readMetadata()
    {
#ifdef DEBUG
        std::cerr << "Exiv2::PsdImage::readMetadata: Reading Photoshop file " << io_->path() << "\n";
#endif
        if (io_->open() != 0)
        {
            throw Error(kerDataSourceOpenFailed, io_->path(), strError());
        }
        IoCloser closer(*io_);
        // Ensure that this is the correct image type
        if (!isPsdType(*io_, false))
        {
            if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
            throw Error(kerNotAnImage, "Photoshop");
        }
        clearMetadata();

        /*
          The Photoshop header goes as follows -- all numbers are in big-endian byte order:

          offset  length   name       description
          ======  =======  =========  =========
           0      4 bytes  signature  always '8BPS'
           4      2 bytes  version    always equal to 1
           6      6 bytes  reserved   must be zero
          12      2 bytes  channels   number of channels in the image, including alpha channels (1 to 24)
          14      4 bytes  rows       the height of the image in pixels
          18      4 bytes  columns    the width of the image in pixels
          22      2 bytes  depth      the number of bits per channel
          24      2 bytes  mode       the color mode of the file; Supported values are: Bitmap=0; Grayscale=1; Indexed=2; RGB=3; CMYK=4; Multichannel=7; Duotone=8; Lab=9
        */
        byte buf[26];
        if (io_->read(buf, 26) != 26)
        {
            throw Error(kerNotAnImage, "Photoshop");
        }
        pixelWidth_ = getLong(buf + 18, bigEndian);
        pixelHeight_ = getLong(buf + 14, bigEndian);

        // immediately following the image header is the color mode data section,
        // the first four bytes of which specify the byte size of the whole section
        if (io_->read(buf, 4) != 4)
        {
            throw Error(kerNotAnImage, "Photoshop");
        }

        // skip it
        uint32_t colorDataLength = getULong(buf, bigEndian);
        if (io_->seek(colorDataLength, BasicIo::cur))
        {
            throw Error(kerNotAnImage, "Photoshop");
        }

        // after the color data section, comes a list of resource blocks, preceded by the total byte size
        if (io_->read(buf, 4) != 4)
        {
            throw Error(kerNotAnImage, "Photoshop");
        }
        uint32_t resourcesLength = getULong(buf, bigEndian);
        while (resourcesLength > 0)
        {
            if (io_->read(buf, 8) != 8)
            {
                throw Error(kerNotAnImage, "Photoshop");
            }

            if (!Photoshop::isIrb(buf, 4))
            {
                break; // bad resource type
            }
            uint16_t resourceId = getUShort(buf + 4, bigEndian);
            uint32_t resourceNameLength = buf[6] & ~1;

            // skip the resource name, plus any padding
            io_->seek(resourceNameLength, BasicIo::cur);

            // read resource size
            if (io_->read(buf, 4) != 4)
            {
                throw Error(kerNotAnImage, "Photoshop");
            }
            uint32_t resourceSize = getULong(buf, bigEndian);
            uint32_t curOffset = io_->tell();

#ifdef DEBUG
        std::cerr << std::hex << "resourceId: " << resourceId << std::dec << " length: " << resourceSize << std::hex << "\n";
#endif

            readResourceBlock(resourceId, resourceSize);
            resourceSize = (resourceSize + 1) & ~1;        // pad to even
            io_->seek(curOffset + resourceSize, BasicIo::beg);
            resourcesLength -= Safe::add(Safe::add(static_cast<uint32_t>(12), resourceNameLength),
                                         resourceSize);
        }

    } // PsdImage::readMetadata

In Exiv2 0.26, Exiv2::IptcParser::decode in iptc.cpp (called from psdimage.cpp in the PSD image reader) may suffer from a denial of service (heap-based buffer over-read) caused by an integer overflow via a crafted PSD image file.


    void PsdImage::readMetadata()
    {
#ifdef DEBUG
        std::cerr << "Exiv2::PsdImage::readMetadata: Reading Photoshop file " << io_->path() << "\n";
#endif
        if (io_->open() != 0)
        {
            throw Error(kerDataSourceOpenFailed, io_->path(), strError());
        }
        IoCloser closer(*io_);
        // Ensure that this is the correct image type
        if (!isPsdType(*io_, false))
        {
            if (io_->error() || io_->eof()) throw Error(kerFailedToReadImageData);
            throw Error(kerNotAnImage, "Photoshop");
        }
        clearMetadata();

        /*
          The Photoshop header goes as follows -- all numbers are in big-endian byte order:

          offset  length   name       description
          ======  =======  =========  =========
           0      4 bytes  signature  always '8BPS'
           4      2 bytes  version    always equal to 1
           6      6 bytes  reserved   must be zero
          12      2 bytes  channels   number of channels in the image, including alpha channels (1 to 24)
          14      4 bytes  rows       the height of the image in pixels
          18      4 bytes  columns    the width of the image in pixels
          22      2 bytes  depth      the number of bits per channel
          24      2 bytes  mode       the color mode of the file; Supported values are: Bitmap=0; Grayscale=1; Indexed=2; RGB=3; CMYK=4; Multichannel=7; Duotone=8; Lab=9
        */
        byte buf[26];
        if (io_->read(buf, 26) != 26)
        {
            throw Error(kerNotAnImage, "Photoshop");
        }
        pixelWidth_ = getLong(buf + 18, bigEndian);
        pixelHeight_ = getLong(buf + 14, bigEndian);

        // immediately following the image header is the color mode data section,
        // the first four bytes of which specify the byte size of the whole section
        if (io_->read(buf, 4) != 4)
        {
            throw Error(kerNotAnImage, "Photoshop");
        }

        // skip it
        uint32_t colorDataLength = getULong(buf, bigEndian);
        if (io_->seek(colorDataLength, BasicIo::cur))
        {
            throw Error(kerNotAnImage, "Photoshop");
        }

        // after the color data section, comes a list of resource blocks, preceded by the total byte size
        if (io_->read(buf, 4) != 4)
        {
            throw Error(kerNotAnImage, "Photoshop");
        }
        uint32_t resourcesLength = getULong(buf, bigEndian);
        enforce(resourcesLength < io_->size(), Exiv2::kerCorruptedMetadata);

        while (resourcesLength > 0)
        {
            if (io_->read(buf, 8) != 8)
            {
                throw Error(kerNotAnImage, "Photoshop");
            }

            if (!Photoshop::isIrb(buf, 4))
            {
                break; // bad resource type
            }
            uint16_t resourceId = getUShort(buf + 4, bigEndian);
            uint32_t resourceNameLength = buf[6] & ~1;

            // skip the resource name, plus any padding
            io_->seek(resourceNameLength, BasicIo::cur);

            // read resource size
            if (io_->read(buf, 4) != 4)
            {
                throw Error(kerNotAnImage, "Photoshop");
            }
            uint32_t resourceSize = getULong(buf, bigEndian);
            uint32_t curOffset = io_->tell();

#ifdef DEBUG
        std::cerr << std::hex << "resourceId: " << resourceId << std::dec << " length: " << resourceSize << std::hex << "\n";
#endif

            readResourceBlock(resourceId, resourceSize);
            resourceSize = (resourceSize + 1) & ~1;        // pad to even
            io_->seek(curOffset + resourceSize, BasicIo::beg);
            resourcesLength -= Safe::add(Safe::add(static_cast<uint32_t>(12), resourceNameLength),
                                         resourceSize);
        }

    } // PsdImage::readMetadata


prevent double-free() on broken full-color images error handling

int LibRaw::unpack(void)
{
    CHECK_ORDER_HIGH(LIBRAW_PROGRESS_LOAD_RAW);
    CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY);
    try {

		if(!libraw_internal_data.internal_data.input)
			return LIBRAW_INPUT_CLOSED;

        RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW,0,2);
        if (O.shot_select >= P1.raw_count)
            return LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE;

        if(!load_raw)
            return LIBRAW_UNSPECIFIED_ERROR;

        if (O.use_camera_matrix && C.cmatrix[0][0] > 0.25)
            {
                memcpy (C.rgb_cam, C.cmatrix, sizeof (C.cmatrix));
                IO.raw_color = 0;
            }
        // already allocated ?
        if(imgdata.image)
            {
                free(imgdata.image);
                imgdata.image = 0;
            }
        if(imgdata.rawdata.raw_alloc)
          {
            free(imgdata.rawdata.raw_alloc);
            imgdata.rawdata.raw_alloc = 0;
          }
        if (libraw_internal_data.unpacker_data.meta_length)
            {
                libraw_internal_data.internal_data.meta_data =
                    (char *) malloc (libraw_internal_data.unpacker_data.meta_length);
                merror (libraw_internal_data.internal_data.meta_data, "LibRaw::unpack()");
            }

        libraw_decoder_info_t decoder_info;
        get_decoder_info(&decoder_info);

        int save_iwidth = S.iwidth, save_iheight = S.iheight, save_shrink = IO.shrink;

        int rwidth = S.raw_width, rheight = S.raw_height;
        if( !IO.fuji_width)
            {
                // adjust non-Fuji allocation
                if(rwidth < S.width + S.left_margin)
                    rwidth = S.width + S.left_margin;
                if(rheight < S.height + S.top_margin)
                    rheight = S.height + S.top_margin;
            }
        S.raw_pitch = S.raw_width*2;
        imgdata.rawdata.raw_image = 0;
        imgdata.rawdata.color4_image = 0;
	imgdata.rawdata.color3_image = 0;
#ifdef USE_RAWSPEED
        // RawSpeed Supported,
        if(O.use_rawspeed && (decoder_info.decoder_flags & LIBRAW_DECODER_TRYRAWSPEED) && _rawspeed_camerameta)
          {
            INT64 spos = ID.input->tell();
            try
              {
                //                printf("Using rawspeed\n");
                ID.input->seek(0,SEEK_SET);
                INT64 _rawspeed_buffer_sz = ID.input->size()+32;
                void *_rawspeed_buffer = malloc(_rawspeed_buffer_sz);
                if(!_rawspeed_buffer) throw LIBRAW_EXCEPTION_ALLOC;
                ID.input->read(_rawspeed_buffer,_rawspeed_buffer_sz,1);
                FileMap map((uchar8*)_rawspeed_buffer,_rawspeed_buffer_sz);
                RawParser t(&map);
                RawDecoder *d = 0;
                CameraMetaDataLR *meta = static_cast<CameraMetaDataLR*>(_rawspeed_camerameta);
                d = t.getDecoder();
                try {
                  d->checkSupport(meta);
                }
                catch (const RawDecoderException& e)
                  {
                    imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_UNSUPPORTED;
                    throw e;
                  }
                d->decodeRaw();
                d->decodeMetaData(meta);
                RawImage r = d->mRaw;
                if (r->isCFA) {
                  // Save pointer to decoder
                  _rawspeed_decoder = static_cast<void*>(d);
                  imgdata.rawdata.raw_image = (ushort*) r->getDataUncropped(0,0);
                  S.raw_pitch = r->pitch;
                  fix_after_rawspeed(r->blackLevel);
                } else if(r->getCpp()==4) {
                  _rawspeed_decoder = static_cast<void*>(d);
                  imgdata.rawdata.color4_image = (ushort(*)[4]) r->getDataUncropped(0,0);
                  S.raw_pitch = r->pitch;
                  C.maximum = r->whitePoint;
                  fix_after_rawspeed(r->blackLevel);
                } else if(r->getCpp() == 3)
                  {
                    _rawspeed_decoder = static_cast<void*>(d);
                    imgdata.rawdata.color3_image = (ushort(*)[3]) r->getDataUncropped(0,0);
                    S.raw_pitch = r->pitch;
                    C.maximum = r->whitePoint;
                    fix_after_rawspeed(r->blackLevel);
                  }
                else
                  {
                    delete d;
                  }
                free(_rawspeed_buffer);
              imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_PROCESSED;
              } catch (...) {
              imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_PROBLEM;
              // no other actions: if raw_image is not set we'll try usual load_raw call
            }
            ID.input->seek(spos,SEEK_SET);
          }
#endif
        if(!imgdata.rawdata.raw_image && !imgdata.rawdata.color4_image && !imgdata.rawdata.color3_image) // RawSpeed failed!
          {
            // Not allocated on RawSpeed call, try call LibRaw
            if(decoder_info.decoder_flags &  LIBRAW_DECODER_FLATFIELD)
              {
                imgdata.rawdata.raw_alloc = malloc(rwidth*(rheight+7)*sizeof(imgdata.rawdata.raw_image[0]));
                imgdata.rawdata.raw_image = (ushort*) imgdata.rawdata.raw_alloc;
              }
            else if (decoder_info.decoder_flags & LIBRAW_DECODER_LEGACY)
              {
                // sRAW and Foveon only, so extra buffer size is just 1/4
                // Legacy converters does not supports half mode!
                S.iwidth = S.width;
                S.iheight= S.height;
                IO.shrink = 0;
				S.raw_pitch = S.width*8;
                // allocate image as temporary buffer, size
                imgdata.rawdata.raw_alloc = calloc(S.iwidth*S.iheight,sizeof(*imgdata.image));
                imgdata.image = (ushort (*)[4]) imgdata.rawdata.raw_alloc;
              }
            ID.input->seek(libraw_internal_data.unpacker_data.data_offset, SEEK_SET);

			unsigned m_save = C.maximum;
			if(load_raw == &LibRaw::unpacked_load_raw && !strcasecmp(imgdata.idata.make,"Nikon"))
				C.maximum=65535;
            (this->*load_raw)();
			if(load_raw == &LibRaw::unpacked_load_raw && !strcasecmp(imgdata.idata.make,"Nikon"))
				C.maximum = m_save;
          }

        if(imgdata.rawdata.raw_image)
          crop_masked_pixels(); // calculate black levels

        // recover saved
        if( (decoder_info.decoder_flags & LIBRAW_DECODER_LEGACY) && !imgdata.rawdata.color4_image)
            {
                imgdata.image = 0;
                imgdata.rawdata.color4_image = (ushort (*)[4]) imgdata.rawdata.raw_alloc;
            }

        // recover image sizes
        S.iwidth = save_iwidth;
        S.iheight = save_iheight;
        IO.shrink = save_shrink;

        // adjust black to possible maximum
        unsigned int i = C.cblack[3];
        unsigned int c;
        for(c=0;c<3;c++)
            if (i > C.cblack[c]) i = C.cblack[c];
        for (c=0;c<4;c++)
            C.cblack[c] -= i;
        C.black += i;

        // Save color,sizes and internal data into raw_image fields
        memmove(&imgdata.rawdata.color,&imgdata.color,sizeof(imgdata.color));
        memmove(&imgdata.rawdata.sizes,&imgdata.sizes,sizeof(imgdata.sizes));
        memmove(&imgdata.rawdata.iparams,&imgdata.idata,sizeof(imgdata.idata));
        memmove(&imgdata.rawdata.ioparams,&libraw_internal_data.internal_output_params,sizeof(libraw_internal_data.internal_output_params));

        SET_PROC_FLAG(LIBRAW_PROGRESS_LOAD_RAW);
        RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW,1,2);

        return 0;
    }
    catch ( LibRaw_exceptions err) {
        EXCEPTION_HANDLER(err);
    }
    catch (std::exception ee) {
        EXCEPTION_HANDLER(LIBRAW_EXCEPTION_IO_CORRUPT);
    }
}

Multiple double free vulnerabilities in the LibRaw::unpack function in libraw_cxx.cpp in LibRaw before 0.15.2 allow context-dependent attackers to cause a denial of service (application crash) and possibly execute arbitrary code via a malformed full-color (1) Foveon or (2) sRAW image file.


int LibRaw::unpack(void)
{
    CHECK_ORDER_HIGH(LIBRAW_PROGRESS_LOAD_RAW);
    CHECK_ORDER_LOW(LIBRAW_PROGRESS_IDENTIFY);
    try {

		if(!libraw_internal_data.internal_data.input)
			return LIBRAW_INPUT_CLOSED;

        RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW,0,2);
        if (O.shot_select >= P1.raw_count)
            return LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE;

        if(!load_raw)
            return LIBRAW_UNSPECIFIED_ERROR;

        if (O.use_camera_matrix && C.cmatrix[0][0] > 0.25)
            {
                memcpy (C.rgb_cam, C.cmatrix, sizeof (C.cmatrix));
                IO.raw_color = 0;
            }
        // already allocated ?
        if(imgdata.image)
            {
                free(imgdata.image);
                imgdata.image = 0;
            }
        if(imgdata.rawdata.raw_alloc)
          {
            free(imgdata.rawdata.raw_alloc);
            imgdata.rawdata.raw_alloc = 0;
          }
        if (libraw_internal_data.unpacker_data.meta_length)
            {
                libraw_internal_data.internal_data.meta_data =
                    (char *) malloc (libraw_internal_data.unpacker_data.meta_length);
                merror (libraw_internal_data.internal_data.meta_data, "LibRaw::unpack()");
            }

        libraw_decoder_info_t decoder_info;
        get_decoder_info(&decoder_info);

        int save_iwidth = S.iwidth, save_iheight = S.iheight, save_shrink = IO.shrink;

        int rwidth = S.raw_width, rheight = S.raw_height;
        if( !IO.fuji_width)
            {
                // adjust non-Fuji allocation
                if(rwidth < S.width + S.left_margin)
                    rwidth = S.width + S.left_margin;
                if(rheight < S.height + S.top_margin)
                    rheight = S.height + S.top_margin;
            }
        S.raw_pitch = S.raw_width*2;
        imgdata.rawdata.raw_image = 0;
        imgdata.rawdata.color4_image = 0;
	imgdata.rawdata.color3_image = 0;
#ifdef USE_RAWSPEED
        // RawSpeed Supported,
        if(O.use_rawspeed && (decoder_info.decoder_flags & LIBRAW_DECODER_TRYRAWSPEED) && _rawspeed_camerameta)
          {
            INT64 spos = ID.input->tell();
            try
              {
                //                printf("Using rawspeed\n");
                ID.input->seek(0,SEEK_SET);
                INT64 _rawspeed_buffer_sz = ID.input->size()+32;
                void *_rawspeed_buffer = malloc(_rawspeed_buffer_sz);
                if(!_rawspeed_buffer) throw LIBRAW_EXCEPTION_ALLOC;
                ID.input->read(_rawspeed_buffer,_rawspeed_buffer_sz,1);
                FileMap map((uchar8*)_rawspeed_buffer,_rawspeed_buffer_sz);
                RawParser t(&map);
                RawDecoder *d = 0;
                CameraMetaDataLR *meta = static_cast<CameraMetaDataLR*>(_rawspeed_camerameta);
                d = t.getDecoder();
                try {
                  d->checkSupport(meta);
                }
                catch (const RawDecoderException& e)
                  {
                    imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_UNSUPPORTED;
                    throw e;
                  }
                d->decodeRaw();
                d->decodeMetaData(meta);
                RawImage r = d->mRaw;
                if (r->isCFA) {
                  // Save pointer to decoder
                  _rawspeed_decoder = static_cast<void*>(d);
                  imgdata.rawdata.raw_image = (ushort*) r->getDataUncropped(0,0);
                  S.raw_pitch = r->pitch;
                  fix_after_rawspeed(r->blackLevel);
                } else if(r->getCpp()==4) {
                  _rawspeed_decoder = static_cast<void*>(d);
                  imgdata.rawdata.color4_image = (ushort(*)[4]) r->getDataUncropped(0,0);
                  S.raw_pitch = r->pitch;
                  C.maximum = r->whitePoint;
                  fix_after_rawspeed(r->blackLevel);
                } else if(r->getCpp() == 3)
                  {
                    _rawspeed_decoder = static_cast<void*>(d);
                    imgdata.rawdata.color3_image = (ushort(*)[3]) r->getDataUncropped(0,0);
                    S.raw_pitch = r->pitch;
                    C.maximum = r->whitePoint;
                    fix_after_rawspeed(r->blackLevel);
                  }
                else
                  {
                    delete d;
                  }
                free(_rawspeed_buffer);
              imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_PROCESSED;
              } catch (...) {
              imgdata.process_warnings |= LIBRAW_WARN_RAWSPEED_PROBLEM;
              // no other actions: if raw_image is not set we'll try usual load_raw call
            }
            ID.input->seek(spos,SEEK_SET);
          }
#endif
        if(!imgdata.rawdata.raw_image && !imgdata.rawdata.color4_image && !imgdata.rawdata.color3_image) // RawSpeed failed!
          {
            // Not allocated on RawSpeed call, try call LibRaw
            if(decoder_info.decoder_flags &  LIBRAW_DECODER_FLATFIELD)
              {
                imgdata.rawdata.raw_alloc = malloc(rwidth*(rheight+7)*sizeof(imgdata.rawdata.raw_image[0]));
                imgdata.rawdata.raw_image = (ushort*) imgdata.rawdata.raw_alloc;
              }
            else if (decoder_info.decoder_flags & LIBRAW_DECODER_LEGACY)
              {
                // sRAW and Foveon only, so extra buffer size is just 1/4
                // Legacy converters does not supports half mode!
                S.iwidth = S.width;
                S.iheight= S.height;
                IO.shrink = 0;
				S.raw_pitch = S.width*8;
                // allocate image as temporary buffer, size
                imgdata.rawdata.raw_alloc = 0;
                imgdata.image = (ushort (*)[4]) calloc(S.iwidth*S.iheight,sizeof(*imgdata.image));
              }
            ID.input->seek(libraw_internal_data.unpacker_data.data_offset, SEEK_SET);

			unsigned m_save = C.maximum;
			if(load_raw == &LibRaw::unpacked_load_raw && !strcasecmp(imgdata.idata.make,"Nikon"))
				C.maximum=65535;
            (this->*load_raw)();
			if(load_raw == &LibRaw::unpacked_load_raw && !strcasecmp(imgdata.idata.make,"Nikon"))
				C.maximum = m_save;
			if (decoder_info.decoder_flags & LIBRAW_DECODER_LEGACY)
			{
				// successfully decoded legacy image, attach image to raw_alloc
				imgdata.rawdata.raw_alloc = imgdata.image;
				imgdata.image = 0;
			}
          }

        if(imgdata.rawdata.raw_image)
          crop_masked_pixels(); // calculate black levels

        // recover saved
        if( (decoder_info.decoder_flags & LIBRAW_DECODER_LEGACY) && !imgdata.rawdata.color4_image)
            {
                imgdata.image = 0;
                imgdata.rawdata.color4_image = (ushort (*)[4]) imgdata.rawdata.raw_alloc;
            }

        // recover image sizes
        S.iwidth = save_iwidth;
        S.iheight = save_iheight;
        IO.shrink = save_shrink;

        // adjust black to possible maximum
        unsigned int i = C.cblack[3];
        unsigned int c;
        for(c=0;c<3;c++)
            if (i > C.cblack[c]) i = C.cblack[c];
        for (c=0;c<4;c++)
            C.cblack[c] -= i;
        C.black += i;

        // Save color,sizes and internal data into raw_image fields
        memmove(&imgdata.rawdata.color,&imgdata.color,sizeof(imgdata.color));
        memmove(&imgdata.rawdata.sizes,&imgdata.sizes,sizeof(imgdata.sizes));
        memmove(&imgdata.rawdata.iparams,&imgdata.idata,sizeof(imgdata.idata));
        memmove(&imgdata.rawdata.ioparams,&libraw_internal_data.internal_output_params,sizeof(libraw_internal_data.internal_output_params));

        SET_PROC_FLAG(LIBRAW_PROGRESS_LOAD_RAW);
        RUN_CALLBACK(LIBRAW_PROGRESS_LOAD_RAW,1,2);

        return 0;
    }
    catch ( LibRaw_exceptions err) {
        EXCEPTION_HANDLER(err);
    }
    catch (std::exception ee) {
        EXCEPTION_HANDLER(LIBRAW_EXCEPTION_IO_CORRUPT);
    }
}


patch 8.2.3847: illegal memory access when using a lambda with an error

Problem:    Illegal memory access when using a lambda with an error.
Solution:   Avoid skipping over the NUL after a string.

eval_lambda(
    char_u	**arg,
    typval_T	*rettv,
    evalarg_T	*evalarg,
    int		verbose)	// give error messages
{
    int		evaluate = evalarg != NULL
				      && (evalarg->eval_flags & EVAL_EVALUATE);
    typval_T	base = *rettv;
    int		ret;

    rettv->v_type = VAR_UNKNOWN;

    if (**arg == '{')
    {
	// ->{lambda}()
	ret = get_lambda_tv(arg, rettv, FALSE, evalarg);
    }
    else
    {
	// ->(lambda)()
	++*arg;
	ret = eval1(arg, rettv, evalarg);
	*arg = skipwhite_and_linebreak(*arg, evalarg);
	if (**arg != ')')
	{
	    emsg(_(e_missing_closing_paren));
	    ret = FAIL;
	}
	++*arg;
    }
    if (ret != OK)
	return FAIL;
    else if (**arg != '(')
    {
	if (verbose)
	{
	    if (*skipwhite(*arg) == '(')
		emsg(_(e_nowhitespace));
	    else
		semsg(_(e_missing_parenthesis_str), "lambda");
	}
	clear_tv(rettv);
	ret = FAIL;
    }
    else
	ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base);

    // Clear the funcref afterwards, so that deleting it while
    // evaluating the arguments is possible (see test55).
    if (evaluate)
	clear_tv(&base);

    return ret;
}

vim is vulnerable to Heap-based Buffer Overflow


eval_lambda(
    char_u	**arg,
    typval_T	*rettv,
    evalarg_T	*evalarg,
    int		verbose)	// give error messages
{
    int		evaluate = evalarg != NULL
				      && (evalarg->eval_flags & EVAL_EVALUATE);
    typval_T	base = *rettv;
    int		ret;

    rettv->v_type = VAR_UNKNOWN;

    if (**arg == '{')
    {
	// ->{lambda}()
	ret = get_lambda_tv(arg, rettv, FALSE, evalarg);
    }
    else
    {
	// ->(lambda)()
	++*arg;
	ret = eval1(arg, rettv, evalarg);
	*arg = skipwhite_and_linebreak(*arg, evalarg);
	if (**arg == ')')
	{
	    ++*arg;
	}
	else
	{
	    emsg(_(e_missing_closing_paren));
	    ret = FAIL;
	}
    }
    if (ret != OK)
	return FAIL;
    else if (**arg != '(')
    {
	if (verbose)
	{
	    if (*skipwhite(*arg) == '(')
		emsg(_(e_nowhitespace));
	    else
		semsg(_(e_missing_parenthesis_str), "lambda");
	}
	clear_tv(rettv);
	ret = FAIL;
    }
    else
	ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base);

    // Clear the funcref afterwards, so that deleting it while
    // evaluating the arguments is possible (see test55).
    if (evaluate)
	clear_tv(&base);

    return ret;
}


pam_env: abort when encountering an overflowed environment variable expansion

* modules/pam_env/pam_env.c (_expand_arg): Abort when encountering an
overflowed environment variable expansion.
Fixes CVE-2011-3149.
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/pam/+bug/874565

static int _expand_arg(pam_handle_t *pamh, char **value)
{
  const char *orig=*value, *tmpptr=NULL;
  char *ptr;       /*
		    * Sure would be nice to use tmpptr but it needs to be
		    * a constant so that the compiler will shut up when I
		    * call pam_getenv and _pam_get_item_byname -- sigh
		    */

  /* No unexpanded variable can be bigger than BUF_SIZE */
  char type, tmpval[BUF_SIZE];

  /* I know this shouldn't be hard-coded but it's so much easier this way */
  char tmp[MAX_ENV];

  D(("Remember to initialize tmp!"));
  memset(tmp, 0, MAX_ENV);

  /*
   * (possibly non-existent) environment variables can be used as values
   * by prepending a "$" and wrapping in {} (ie: ${HOST}), can escape with "\"
   * (possibly non-existent) PAM items can be used as values
   * by prepending a "@" and wrapping in {} (ie: @{PAM_RHOST}, can escape
   *
   */
  D(("Expanding <%s>",orig));
  while (*orig) {     /* while there is some input to deal with */
    if ('\\' == *orig) {
      ++orig;
      if ('$' != *orig && '@' != *orig) {
	D(("Unrecognized escaped character: <%c> - ignoring", *orig));
	pam_syslog(pamh, LOG_ERR,
		   "Unrecognized escaped character: <%c> - ignoring",
		   *orig);
      } else if ((strlen(tmp) + 1) < MAX_ENV) {
	tmp[strlen(tmp)] = *orig++;        /* Note the increment */
      } else {
	/* is it really a good idea to try to log this? */
	D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
	pam_syslog (pamh, LOG_ERR, "Variable buffer overflow: <%s> + <%s>",
		 tmp, tmpptr);
      }
      continue;
    }
    if ('$' == *orig || '@' == *orig) {
      if ('{' != *(orig+1)) {
	D(("Expandable variables must be wrapped in {}"
	   " <%s> - ignoring", orig));
	pam_syslog(pamh, LOG_ERR, "Expandable variables must be wrapped in {}"
		 " <%s> - ignoring", orig);
	if ((strlen(tmp) + 1) < MAX_ENV) {
	  tmp[strlen(tmp)] = *orig++;        /* Note the increment */
	}
	continue;
      } else {
	D(("Expandable argument: <%s>", orig));
	type = *orig;
	orig+=2;     /* skip the ${ or @{ characters */
	ptr = strchr(orig, '}');
	if (ptr) {
	  *ptr++ = '\0';
	} else {
	  D(("Unterminated expandable variable: <%s>", orig-2));
	  pam_syslog(pamh, LOG_ERR,
		     "Unterminated expandable variable: <%s>", orig-2);
	  return PAM_ABORT;
	}
	strncpy(tmpval, orig, sizeof(tmpval));
	tmpval[sizeof(tmpval)-1] = '\0';
	orig=ptr;
	/*
	 * so, we know we need to expand tmpval, it is either
	 * an environment variable or a PAM_ITEM. type will tell us which
	 */
	switch (type) {

	case '$':
	  D(("Expanding env var: <%s>",tmpval));
	  tmpptr = pam_getenv(pamh, tmpval);
	  D(("Expanded to <%s>", tmpptr));
	  break;

	case '@':
	  D(("Expanding pam item: <%s>",tmpval));
	  tmpptr = _pam_get_item_byname(pamh, tmpval);
	  D(("Expanded to <%s>", tmpptr));
	  break;

	default:
	  D(("Impossible error, type == <%c>", type));
	  pam_syslog(pamh, LOG_CRIT, "Impossible error, type == <%c>", type);
	  return PAM_ABORT;
	}         /* switch */

	if (tmpptr) {
	  if ((strlen(tmp) + strlen(tmpptr)) < MAX_ENV) {
	    strcat(tmp, tmpptr);
	  } else {
	    /* is it really a good idea to try to log this? */
	    D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
	    pam_syslog (pamh, LOG_ERR,
			"Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
	  }
	}
      }           /* if ('{' != *orig++) */
    } else {      /* if ( '$' == *orig || '@' == *orig) */
      if ((strlen(tmp) + 1) < MAX_ENV) {
	tmp[strlen(tmp)] = *orig++;        /* Note the increment */
      } else {
	/* is it really a good idea to try to log this? */
	D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
	pam_syslog(pamh, LOG_ERR,
		   "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
      }
    }
  }              /* for (;*orig;) */

  if (strlen(tmp) > strlen(*value)) {
    free(*value);
    if ((*value = malloc(strlen(tmp) +1)) == NULL) {
      D(("Couldn't malloc %d bytes for expanded var", strlen(tmp)+1));
      pam_syslog (pamh, LOG_ERR, "Couldn't malloc %lu bytes for expanded var",
	       (unsigned long)strlen(tmp)+1);
      return PAM_BUF_ERR;
    }
  }
  strcpy(*value, tmp);
  memset(tmp,'\0',sizeof(tmp));
  D(("Exit."));

  return PAM_SUCCESS;
}

The _expand_arg function in the pam_env module (modules/pam_env/pam_env.c) in Linux-PAM (aka pam) before 1.1.5 does not properly handle when environment variable expansion can overflow, which allows local users to cause a denial of service (CPU consumption).


static int _expand_arg(pam_handle_t *pamh, char **value)
{
  const char *orig=*value, *tmpptr=NULL;
  char *ptr;       /*
		    * Sure would be nice to use tmpptr but it needs to be
		    * a constant so that the compiler will shut up when I
		    * call pam_getenv and _pam_get_item_byname -- sigh
		    */

  /* No unexpanded variable can be bigger than BUF_SIZE */
  char type, tmpval[BUF_SIZE];

  /* I know this shouldn't be hard-coded but it's so much easier this way */
  char tmp[MAX_ENV];

  D(("Remember to initialize tmp!"));
  memset(tmp, 0, MAX_ENV);

  /*
   * (possibly non-existent) environment variables can be used as values
   * by prepending a "$" and wrapping in {} (ie: ${HOST}), can escape with "\"
   * (possibly non-existent) PAM items can be used as values
   * by prepending a "@" and wrapping in {} (ie: @{PAM_RHOST}, can escape
   *
   */
  D(("Expanding <%s>",orig));
  while (*orig) {     /* while there is some input to deal with */
    if ('\\' == *orig) {
      ++orig;
      if ('$' != *orig && '@' != *orig) {
	D(("Unrecognized escaped character: <%c> - ignoring", *orig));
	pam_syslog(pamh, LOG_ERR,
		   "Unrecognized escaped character: <%c> - ignoring",
		   *orig);
      } else if ((strlen(tmp) + 1) < MAX_ENV) {
	tmp[strlen(tmp)] = *orig++;        /* Note the increment */
      } else {
	/* is it really a good idea to try to log this? */
	D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
	pam_syslog (pamh, LOG_ERR, "Variable buffer overflow: <%s> + <%s>",
		 tmp, tmpptr);
	return PAM_BUF_ERR;
      }
      continue;
    }
    if ('$' == *orig || '@' == *orig) {
      if ('{' != *(orig+1)) {
	D(("Expandable variables must be wrapped in {}"
	   " <%s> - ignoring", orig));
	pam_syslog(pamh, LOG_ERR, "Expandable variables must be wrapped in {}"
		 " <%s> - ignoring", orig);
	if ((strlen(tmp) + 1) < MAX_ENV) {
	  tmp[strlen(tmp)] = *orig++;        /* Note the increment */
	}
	continue;
      } else {
	D(("Expandable argument: <%s>", orig));
	type = *orig;
	orig+=2;     /* skip the ${ or @{ characters */
	ptr = strchr(orig, '}');
	if (ptr) {
	  *ptr++ = '\0';
	} else {
	  D(("Unterminated expandable variable: <%s>", orig-2));
	  pam_syslog(pamh, LOG_ERR,
		     "Unterminated expandable variable: <%s>", orig-2);
	  return PAM_ABORT;
	}
	strncpy(tmpval, orig, sizeof(tmpval));
	tmpval[sizeof(tmpval)-1] = '\0';
	orig=ptr;
	/*
	 * so, we know we need to expand tmpval, it is either
	 * an environment variable or a PAM_ITEM. type will tell us which
	 */
	switch (type) {

	case '$':
	  D(("Expanding env var: <%s>",tmpval));
	  tmpptr = pam_getenv(pamh, tmpval);
	  D(("Expanded to <%s>", tmpptr));
	  break;

	case '@':
	  D(("Expanding pam item: <%s>",tmpval));
	  tmpptr = _pam_get_item_byname(pamh, tmpval);
	  D(("Expanded to <%s>", tmpptr));
	  break;

	default:
	  D(("Impossible error, type == <%c>", type));
	  pam_syslog(pamh, LOG_CRIT, "Impossible error, type == <%c>", type);
	  return PAM_ABORT;
	}         /* switch */

	if (tmpptr) {
	  if ((strlen(tmp) + strlen(tmpptr)) < MAX_ENV) {
	    strcat(tmp, tmpptr);
	  } else {
	    /* is it really a good idea to try to log this? */
	    D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
	    pam_syslog (pamh, LOG_ERR,
			"Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
	    return PAM_BUF_ERR;
	  }
	}
      }           /* if ('{' != *orig++) */
    } else {      /* if ( '$' == *orig || '@' == *orig) */
      if ((strlen(tmp) + 1) < MAX_ENV) {
	tmp[strlen(tmp)] = *orig++;        /* Note the increment */
      } else {
	/* is it really a good idea to try to log this? */
	D(("Variable buffer overflow: <%s> + <%s>", tmp, tmpptr));
	pam_syslog(pamh, LOG_ERR,
		   "Variable buffer overflow: <%s> + <%s>", tmp, tmpptr);
	return PAM_BUF_ERR;
      }
    }
  }              /* for (;*orig;) */

  if (strlen(tmp) > strlen(*value)) {
    free(*value);
    if ((*value = malloc(strlen(tmp) +1)) == NULL) {
      D(("Couldn't malloc %d bytes for expanded var", strlen(tmp)+1));
      pam_syslog (pamh, LOG_ERR, "Couldn't malloc %lu bytes for expanded var",
	       (unsigned long)strlen(tmp)+1);
      return PAM_BUF_ERR;
    }
  }
  strcpy(*value, tmp);
  memset(tmp,'\0',sizeof(tmp));
  D(("Exit."));

  return PAM_SUCCESS;
}