PCD decoder overruns the shuffle buffer, Fixes #568

ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
    int x;
    int chunk;
    UINT8* out;
    UINT8* ptr;

    ptr = buf;

    chunk = 3 * state->xsize;

    for (;;) {

	/* We need data for two full lines before we can do anything */
	if (bytes < chunk)
	    return ptr - buf;

	/* Unpack first line */
	out = state->buffer;
	for (x = 0; x < state->xsize; x++) {
	    out[0] = ptr[x];
	    out[1] = ptr[(x+4*state->xsize)/2];
	    out[2] = ptr[(x+5*state->xsize)/2];
	    out += 4;
	}

	state->shuffle((UINT8*) im->image[state->y],
		       state->buffer, state->xsize);

	if (++state->y >= state->ysize)
	    return -1; /* This can hardly happen */

	/* Unpack second line */
	out = state->buffer;
	for (x = 0; x < state->xsize; x++) {
	    out[0] = ptr[x+state->xsize];
	    out[1] = ptr[(x+4*state->xsize)/2];
	    out[2] = ptr[(x+5*state->xsize)/2];
	    out += 4;
	}

	state->shuffle((UINT8*) im->image[state->y],
		       state->buffer, state->xsize);

	if (++state->y >= state->ysize)
	    return -1;

	ptr += chunk;
	bytes -= chunk;

    }
}

Buffer overflow in the ImagingPcdDecode function in PcdDecode.c in Pillow before 3.1.1 and Python Imaging Library (PIL) 1.1.7 and earlier allows remote attackers to cause a denial of service (crash) via a crafted PhotoCD file.


ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
    int x;
    int chunk;
    UINT8* out;
    UINT8* ptr;

    ptr = buf;

    chunk = 3 * state->xsize;

    for (;;) {

	/* We need data for two full lines before we can do anything */
	if (bytes < chunk)
	    return ptr - buf;

	/* Unpack first line */
	out = state->buffer;
	for (x = 0; x < state->xsize; x++) {
	    out[0] = ptr[x];
	    out[1] = ptr[(x+4*state->xsize)/2];
	    out[2] = ptr[(x+5*state->xsize)/2];
	    out += 3;
	}

	state->shuffle((UINT8*) im->image[state->y],
		       state->buffer, state->xsize);

	if (++state->y >= state->ysize)
	    return -1; /* This can hardly happen */

	/* Unpack second line */
	out = state->buffer;
	for (x = 0; x < state->xsize; x++) {
	    out[0] = ptr[x+state->xsize];
	    out[1] = ptr[(x+4*state->xsize)/2];
	    out[2] = ptr[(x+5*state->xsize)/2];
	    out += 3;
	}

	state->shuffle((UINT8*) im->image[state->y],
		       state->buffer, state->xsize);

	if (++state->y >= state->ysize)
	    return -1;

	ptr += chunk;
	bytes -= chunk;

    }
}


CVE-2018-6340 Fix buffer overread

Summary:
snprintf() returns the number of would be written bytes if the buffer had
enough size, letting the original code to construct output string from
arbitrary data on the stack.

Fix this by properly assembling the String without trying to arbitrarily
limiting the length to 30 bytes.

Reviewed By: ottoni

Differential Revision: D13509547

fbshipit-source-id: aa401b83d356f5b261433c4b16d777948ba7f9b4

static Array HHVM_METHOD(Memcache, getextendedstats,
                         const String& /*type*/ /* = null_string */,
                         int /*slabid*/ /* = 0 */, int /*limit*/ /* = 100 */) {
  auto data = Native::data<MemcacheData>(this_);
  memcached_return_t ret;
  memcached_stat_st *stats;

  stats = memcached_stat(&data->m_memcache, nullptr, &ret);
  if (ret != MEMCACHED_SUCCESS) {
    return Array();
  }

  int server_count = memcached_server_count(&data->m_memcache);

  Array return_val;

  for (int server_id = 0; server_id < server_count; server_id++) {
    memcached_stat_st *stat;
    char stats_key[30] = {0};
    size_t key_len;

    LMCD_SERVER_POSITION_INSTANCE_TYPE instance =
      memcached_server_instance_by_position(&data->m_memcache, server_id);
    const char *hostname = LMCD_SERVER_HOSTNAME(instance);
    in_port_t port = LMCD_SERVER_PORT(instance);

    stat = stats + server_id;

    Array server_stats = memcache_build_stats(&data->m_memcache, stat, &ret);
    if (ret != MEMCACHED_SUCCESS) {
      continue;
    }

    key_len = snprintf(stats_key, sizeof(stats_key), "%s:%d", hostname, port);

    return_val.set(String(stats_key, key_len, CopyString), server_stats);
  }

  free(stats);
  return return_val;
}

The Memcache::getextendedstats function can be used to trigger an out-of-bounds read. Exploiting this issue requires control over memcached server hostnames and/or ports. This affects all supported versions of HHVM (3.30 and 3.27.4 and below).


static Array HHVM_METHOD(Memcache, getextendedstats,
                         const String& /*type*/ /* = null_string */,
                         int /*slabid*/ /* = 0 */, int /*limit*/ /* = 100 */) {
  auto data = Native::data<MemcacheData>(this_);
  memcached_return_t ret;
  memcached_stat_st *stats;

  stats = memcached_stat(&data->m_memcache, nullptr, &ret);
  if (ret != MEMCACHED_SUCCESS) {
    return Array();
  }

  int server_count = memcached_server_count(&data->m_memcache);

  Array return_val;

  for (int server_id = 0; server_id < server_count; server_id++) {
    memcached_stat_st *stat;
    LMCD_SERVER_POSITION_INSTANCE_TYPE instance =
      memcached_server_instance_by_position(&data->m_memcache, server_id);
    const char *hostname = LMCD_SERVER_HOSTNAME(instance);
    in_port_t port = LMCD_SERVER_PORT(instance);

    stat = stats + server_id;

    Array server_stats = memcache_build_stats(&data->m_memcache, stat, &ret);
    if (ret != MEMCACHED_SUCCESS) {
      continue;
    }

    auto const port_str = folly::to<std::string>(port);
    auto const key_len = strlen(hostname) + 1 + port_str.length();
    auto key = String(key_len, ReserveString);
    key += hostname;
    key += ":";
    key += port_str;
    return_val.set(key, server_stats);
  }

  free(stats);
  return return_val;
}


Check for multiplication overflow in sfconvert

Checks that a multiplication doesn't overflow when
calculating the buffer size, and if it overflows,
reduce the buffer size instead of failing.

This fixes the 00192-audiofile-signintoverflow-sfconvert case
in #41

bool copyaudiodata (AFfilehandle infile, AFfilehandle outfile, int trackid)
{
	int frameSize = afGetVirtualFrameSize(infile, trackid, 1);

	const int kBufferFrameCount = 65536;
	void *buffer = malloc(kBufferFrameCount * frameSize);

	AFframecount totalFrames = afGetFrameCount(infile, AF_DEFAULT_TRACK);
	AFframecount totalFramesWritten = 0;

	bool success = true;

	while (totalFramesWritten < totalFrames)
	{
		AFframecount framesToRead = totalFrames - totalFramesWritten;
		if (framesToRead > kBufferFrameCount)
			framesToRead = kBufferFrameCount;

		AFframecount framesRead = afReadFrames(infile, trackid, buffer,
			framesToRead);

		if (framesRead < framesToRead)
		{
			fprintf(stderr, "Bad read of audio track data.\n");
			success = false;
			break;
		}

		AFframecount framesWritten = afWriteFrames(outfile, trackid, buffer,
			framesRead);

		if (framesWritten < framesRead)
		{
			fprintf(stderr, "Bad write of audio track data.\n");
			success = false;
			break;
		}

		totalFramesWritten += framesWritten;
	}

	free(buffer);

	return success;
}

Integer overflow in sfcommands/sfconvert.c in Audio File Library (aka audiofile) 0.3.6 allows remote attackers to cause a denial of service (crash) via a crafted file.


bool copyaudiodata (AFfilehandle infile, AFfilehandle outfile, int trackid)
{
	int frameSize = afGetVirtualFrameSize(infile, trackid, 1);

	int kBufferFrameCount = 65536;
	int bufferSize;
	while (multiplyCheckOverflow(kBufferFrameCount, frameSize, &bufferSize))
		kBufferFrameCount /= 2;
	void *buffer = malloc(bufferSize);

	AFframecount totalFrames = afGetFrameCount(infile, AF_DEFAULT_TRACK);
	AFframecount totalFramesWritten = 0;

	bool success = true;

	while (totalFramesWritten < totalFrames)
	{
		AFframecount framesToRead = totalFrames - totalFramesWritten;
		if (framesToRead > kBufferFrameCount)
			framesToRead = kBufferFrameCount;

		AFframecount framesRead = afReadFrames(infile, trackid, buffer,
			framesToRead);

		if (framesRead < framesToRead)
		{
			fprintf(stderr, "Bad read of audio track data.\n");
			success = false;
			break;
		}

		AFframecount framesWritten = afWriteFrames(outfile, trackid, buffer,
			framesRead);

		if (framesWritten < framesRead)
		{
			fprintf(stderr, "Bad write of audio track data.\n");
			success = false;
			break;
		}

		totalFramesWritten += framesWritten;
	}

	free(buffer);

	return success;
}


Always check the number of coefficients

When building the library with NDEBUG, asserts are eliminated
so it's better to always check that the number of coefficients
is inside the array range.

This fixes the 00191-audiofile-indexoob issue in #41

status WAVEFile::parseFormat(const Tag &id, uint32_t size)
{
	Track *track = getTrack();

	uint16_t formatTag;
	readU16(&formatTag);
	uint16_t channelCount;
	readU16(&channelCount);
	uint32_t sampleRate;
	readU32(&sampleRate);
	uint32_t averageBytesPerSecond;
	readU32(&averageBytesPerSecond);
	uint16_t blockAlign;
	readU16(&blockAlign);

	if (!channelCount)
	{
		_af_error(AF_BAD_CHANNELS, "invalid file with 0 channels");
		return AF_FAIL;
	}

	track->f.channelCount = channelCount;
	track->f.sampleRate = sampleRate;
	track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN;

	/* Default to uncompressed audio data. */
	track->f.compressionType = AF_COMPRESSION_NONE;
	track->f.framesPerPacket = 1;

	switch (formatTag)
	{
		case WAVE_FORMAT_PCM:
		{
			uint16_t bitsPerSample;
			readU16(&bitsPerSample);

			track->f.sampleWidth = bitsPerSample;

			if (bitsPerSample == 0 || bitsPerSample > 32)
			{
				_af_error(AF_BAD_WIDTH,
					"bad sample width of %d bits",
					bitsPerSample);
				return AF_FAIL;
			}

			if (bitsPerSample <= 8)
				track->f.sampleFormat = AF_SAMPFMT_UNSIGNED;
			else
				track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
		}
		break;

		case WAVE_FORMAT_MULAW:
		case IBM_FORMAT_MULAW:
			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;
			track->f.compressionType = AF_COMPRESSION_G711_ULAW;
			track->f.bytesPerPacket = track->f.channelCount;
			break;

		case WAVE_FORMAT_ALAW:
		case IBM_FORMAT_ALAW:
			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;
			track->f.compressionType = AF_COMPRESSION_G711_ALAW;
			track->f.bytesPerPacket = track->f.channelCount;
			break;

		case WAVE_FORMAT_IEEE_FLOAT:
		{
			uint16_t bitsPerSample;
			readU16(&bitsPerSample);

			if (bitsPerSample == 64)
			{
				track->f.sampleWidth = 64;
				track->f.sampleFormat = AF_SAMPFMT_DOUBLE;
			}
			else
			{
				track->f.sampleWidth = 32;
				track->f.sampleFormat = AF_SAMPFMT_FLOAT;
			}
		}
		break;

		case WAVE_FORMAT_ADPCM:
		{
			uint16_t bitsPerSample, extraByteCount,
					samplesPerBlock, numCoefficients;

			if (track->f.channelCount != 1 &&
				track->f.channelCount != 2)
			{
				_af_error(AF_BAD_CHANNELS,
					"WAVE file with MS ADPCM compression "
					"must have 1 or 2 channels");
			}

			readU16(&bitsPerSample);
			readU16(&extraByteCount);
			readU16(&samplesPerBlock);
			readU16(&numCoefficients);

			/* numCoefficients should be at least 7. */
			assert(numCoefficients >= 7 && numCoefficients <= 255);

			m_msadpcmNumCoefficients = numCoefficients;

			for (int i=0; i<m_msadpcmNumCoefficients; i++)
			{
				readS16(&m_msadpcmCoefficients[i][0]);
				readS16(&m_msadpcmCoefficients[i][1]);
			}

			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.compressionType = AF_COMPRESSION_MS_ADPCM;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;

			track->f.framesPerPacket = samplesPerBlock;
			track->f.bytesPerPacket = blockAlign;

			// Create the parameter list.
			AUpvlist pv = AUpvnew(2);
			AUpvsetparam(pv, 0, _AF_MS_ADPCM_NUM_COEFFICIENTS);
			AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG);
			long l = m_msadpcmNumCoefficients;
			AUpvsetval(pv, 0, &l);

			AUpvsetparam(pv, 1, _AF_MS_ADPCM_COEFFICIENTS);
			AUpvsetvaltype(pv, 1, AU_PVTYPE_PTR);
			void *v = m_msadpcmCoefficients;
			AUpvsetval(pv, 1, &v);

			track->f.compressionParams = pv;
		}
		break;

		case WAVE_FORMAT_DVI_ADPCM:
		{
			uint16_t bitsPerSample, extraByteCount, samplesPerBlock;

			readU16(&bitsPerSample);
			readU16(&extraByteCount);
			readU16(&samplesPerBlock);

			if (bitsPerSample != 4)
			{
				_af_error(AF_BAD_NOT_IMPLEMENTED,
					"IMA ADPCM compression supports only 4 bits per sample");
			}

			int bytesPerBlock = (samplesPerBlock + 14) / 8 * 4 * channelCount;
			if (bytesPerBlock > blockAlign || (samplesPerBlock % 8) != 1)
			{
				_af_error(AF_BAD_CODEC_CONFIG,
					"Invalid samples per block for IMA ADPCM compression");
			}

			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.compressionType = AF_COMPRESSION_IMA;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;

			initIMACompressionParams();

			track->f.framesPerPacket = samplesPerBlock;
			track->f.bytesPerPacket = blockAlign;
		}
		break;

		case WAVE_FORMAT_EXTENSIBLE:
		{
			uint16_t bitsPerSample;
			readU16(&bitsPerSample);
			uint16_t extraByteCount;
			readU16(&extraByteCount);
			uint16_t reserved;
			readU16(&reserved);
			uint32_t channelMask;
			readU32(&channelMask);
			UUID subformat;
			readUUID(&subformat);
			if (subformat == _af_wave_guid_pcm)
			{
				track->f.sampleWidth = bitsPerSample;

				if (bitsPerSample == 0 || bitsPerSample > 32)
				{
					_af_error(AF_BAD_WIDTH,
						"bad sample width of %d bits",
						bitsPerSample);
					return AF_FAIL;
				}

				// Use valid bits per sample if bytes per sample is the same.
				if (reserved <= bitsPerSample &&
					(reserved + 7) / 8 == (bitsPerSample + 7) / 8)
					track->f.sampleWidth = reserved;

				if (bitsPerSample <= 8)
					track->f.sampleFormat = AF_SAMPFMT_UNSIGNED;
				else
					track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			}
			else if (subformat == _af_wave_guid_ieee_float)
			{
				if (bitsPerSample == 64)
				{
					track->f.sampleWidth = 64;
					track->f.sampleFormat = AF_SAMPFMT_DOUBLE;
				}
				else
				{
					track->f.sampleWidth = 32;
					track->f.sampleFormat = AF_SAMPFMT_FLOAT;
				}
			}
			else if (subformat == _af_wave_guid_alaw ||
				subformat == _af_wave_guid_ulaw)
			{
				track->f.compressionType = subformat == _af_wave_guid_alaw ?
					AF_COMPRESSION_G711_ALAW : AF_COMPRESSION_G711_ULAW;
				track->f.sampleWidth = 16;
				track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
				track->f.byteOrder = _AF_BYTEORDER_NATIVE;
				track->f.bytesPerPacket = channelCount;
			}
			else
			{
				_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE extensible data format %s is not currently supported", subformat.name().c_str());
				return AF_FAIL;
			}
		}
		break;

		case WAVE_FORMAT_YAMAHA_ADPCM:
		case WAVE_FORMAT_OKI_ADPCM:
		case WAVE_FORMAT_CREATIVE_ADPCM:
		case IBM_FORMAT_ADPCM:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE ADPCM data format 0x%x is not currently supported", formatTag);
			return AF_FAIL;
			break;

		case WAVE_FORMAT_MPEG:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG data format is not supported");
			return AF_FAIL;
			break;

		case WAVE_FORMAT_MPEGLAYER3:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG layer 3 data format is not supported");
			return AF_FAIL;
			break;

		default:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE file data format 0x%x not currently supported != 0xfffe ? %d, != EXTENSIBLE? %d", formatTag, formatTag != 0xfffe, formatTag != WAVE_FORMAT_EXTENSIBLE);
			return AF_FAIL;
			break;
	}

	if (track->f.isUncompressed())
		track->f.computeBytesPerPacketPCM();

	_af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth);

	return AF_SUCCEED;
}

WAVE.cpp in Audio File Library (aka audiofile) 0.3.6 allows remote attackers to cause a denial of service (crash) via vectors related to a large number of coefficients.


status WAVEFile::parseFormat(const Tag &id, uint32_t size)
{
	Track *track = getTrack();

	uint16_t formatTag;
	readU16(&formatTag);
	uint16_t channelCount;
	readU16(&channelCount);
	uint32_t sampleRate;
	readU32(&sampleRate);
	uint32_t averageBytesPerSecond;
	readU32(&averageBytesPerSecond);
	uint16_t blockAlign;
	readU16(&blockAlign);

	if (!channelCount)
	{
		_af_error(AF_BAD_CHANNELS, "invalid file with 0 channels");
		return AF_FAIL;
	}

	track->f.channelCount = channelCount;
	track->f.sampleRate = sampleRate;
	track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN;

	/* Default to uncompressed audio data. */
	track->f.compressionType = AF_COMPRESSION_NONE;
	track->f.framesPerPacket = 1;

	switch (formatTag)
	{
		case WAVE_FORMAT_PCM:
		{
			uint16_t bitsPerSample;
			readU16(&bitsPerSample);

			track->f.sampleWidth = bitsPerSample;

			if (bitsPerSample == 0 || bitsPerSample > 32)
			{
				_af_error(AF_BAD_WIDTH,
					"bad sample width of %d bits",
					bitsPerSample);
				return AF_FAIL;
			}

			if (bitsPerSample <= 8)
				track->f.sampleFormat = AF_SAMPFMT_UNSIGNED;
			else
				track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
		}
		break;

		case WAVE_FORMAT_MULAW:
		case IBM_FORMAT_MULAW:
			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;
			track->f.compressionType = AF_COMPRESSION_G711_ULAW;
			track->f.bytesPerPacket = track->f.channelCount;
			break;

		case WAVE_FORMAT_ALAW:
		case IBM_FORMAT_ALAW:
			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;
			track->f.compressionType = AF_COMPRESSION_G711_ALAW;
			track->f.bytesPerPacket = track->f.channelCount;
			break;

		case WAVE_FORMAT_IEEE_FLOAT:
		{
			uint16_t bitsPerSample;
			readU16(&bitsPerSample);

			if (bitsPerSample == 64)
			{
				track->f.sampleWidth = 64;
				track->f.sampleFormat = AF_SAMPFMT_DOUBLE;
			}
			else
			{
				track->f.sampleWidth = 32;
				track->f.sampleFormat = AF_SAMPFMT_FLOAT;
			}
		}
		break;

		case WAVE_FORMAT_ADPCM:
		{
			uint16_t bitsPerSample, extraByteCount,
					samplesPerBlock, numCoefficients;

			if (track->f.channelCount != 1 &&
				track->f.channelCount != 2)
			{
				_af_error(AF_BAD_CHANNELS,
					"WAVE file with MS ADPCM compression "
					"must have 1 or 2 channels");
			}

			readU16(&bitsPerSample);
			readU16(&extraByteCount);
			readU16(&samplesPerBlock);
			readU16(&numCoefficients);

			/* numCoefficients should be at least 7. */
			assert(numCoefficients >= 7 && numCoefficients <= 255);
			if (numCoefficients < 7 || numCoefficients > 255)
			{
				_af_error(AF_BAD_HEADER,
						"Bad number of coefficients");
				return AF_FAIL;
			}

			m_msadpcmNumCoefficients = numCoefficients;

			for (int i=0; i<m_msadpcmNumCoefficients; i++)
			{
				readS16(&m_msadpcmCoefficients[i][0]);
				readS16(&m_msadpcmCoefficients[i][1]);
			}

			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.compressionType = AF_COMPRESSION_MS_ADPCM;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;

			track->f.framesPerPacket = samplesPerBlock;
			track->f.bytesPerPacket = blockAlign;

			// Create the parameter list.
			AUpvlist pv = AUpvnew(2);
			AUpvsetparam(pv, 0, _AF_MS_ADPCM_NUM_COEFFICIENTS);
			AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG);
			long l = m_msadpcmNumCoefficients;
			AUpvsetval(pv, 0, &l);

			AUpvsetparam(pv, 1, _AF_MS_ADPCM_COEFFICIENTS);
			AUpvsetvaltype(pv, 1, AU_PVTYPE_PTR);
			void *v = m_msadpcmCoefficients;
			AUpvsetval(pv, 1, &v);

			track->f.compressionParams = pv;
		}
		break;

		case WAVE_FORMAT_DVI_ADPCM:
		{
			uint16_t bitsPerSample, extraByteCount, samplesPerBlock;

			readU16(&bitsPerSample);
			readU16(&extraByteCount);
			readU16(&samplesPerBlock);

			if (bitsPerSample != 4)
			{
				_af_error(AF_BAD_NOT_IMPLEMENTED,
					"IMA ADPCM compression supports only 4 bits per sample");
			}

			int bytesPerBlock = (samplesPerBlock + 14) / 8 * 4 * channelCount;
			if (bytesPerBlock > blockAlign || (samplesPerBlock % 8) != 1)
			{
				_af_error(AF_BAD_CODEC_CONFIG,
					"Invalid samples per block for IMA ADPCM compression");
			}

			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.compressionType = AF_COMPRESSION_IMA;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;

			initIMACompressionParams();

			track->f.framesPerPacket = samplesPerBlock;
			track->f.bytesPerPacket = blockAlign;
		}
		break;

		case WAVE_FORMAT_EXTENSIBLE:
		{
			uint16_t bitsPerSample;
			readU16(&bitsPerSample);
			uint16_t extraByteCount;
			readU16(&extraByteCount);
			uint16_t reserved;
			readU16(&reserved);
			uint32_t channelMask;
			readU32(&channelMask);
			UUID subformat;
			readUUID(&subformat);
			if (subformat == _af_wave_guid_pcm)
			{
				track->f.sampleWidth = bitsPerSample;

				if (bitsPerSample == 0 || bitsPerSample > 32)
				{
					_af_error(AF_BAD_WIDTH,
						"bad sample width of %d bits",
						bitsPerSample);
					return AF_FAIL;
				}

				// Use valid bits per sample if bytes per sample is the same.
				if (reserved <= bitsPerSample &&
					(reserved + 7) / 8 == (bitsPerSample + 7) / 8)
					track->f.sampleWidth = reserved;

				if (bitsPerSample <= 8)
					track->f.sampleFormat = AF_SAMPFMT_UNSIGNED;
				else
					track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			}
			else if (subformat == _af_wave_guid_ieee_float)
			{
				if (bitsPerSample == 64)
				{
					track->f.sampleWidth = 64;
					track->f.sampleFormat = AF_SAMPFMT_DOUBLE;
				}
				else
				{
					track->f.sampleWidth = 32;
					track->f.sampleFormat = AF_SAMPFMT_FLOAT;
				}
			}
			else if (subformat == _af_wave_guid_alaw ||
				subformat == _af_wave_guid_ulaw)
			{
				track->f.compressionType = subformat == _af_wave_guid_alaw ?
					AF_COMPRESSION_G711_ALAW : AF_COMPRESSION_G711_ULAW;
				track->f.sampleWidth = 16;
				track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
				track->f.byteOrder = _AF_BYTEORDER_NATIVE;
				track->f.bytesPerPacket = channelCount;
			}
			else
			{
				_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE extensible data format %s is not currently supported", subformat.name().c_str());
				return AF_FAIL;
			}
		}
		break;

		case WAVE_FORMAT_YAMAHA_ADPCM:
		case WAVE_FORMAT_OKI_ADPCM:
		case WAVE_FORMAT_CREATIVE_ADPCM:
		case IBM_FORMAT_ADPCM:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE ADPCM data format 0x%x is not currently supported", formatTag);
			return AF_FAIL;
			break;

		case WAVE_FORMAT_MPEG:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG data format is not supported");
			return AF_FAIL;
			break;

		case WAVE_FORMAT_MPEGLAYER3:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG layer 3 data format is not supported");
			return AF_FAIL;
			break;

		default:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE file data format 0x%x not currently supported != 0xfffe ? %d, != EXTENSIBLE? %d", formatTag, formatTag != 0xfffe, formatTag != WAVE_FORMAT_EXTENSIBLE);
			return AF_FAIL;
			break;
	}

	if (track->f.isUncompressed())
		track->f.computeBytesPerPacketPCM();

	_af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth);

	return AF_SUCCEED;
}


AP4_DataAtom::AP4_DataAtom(AP4_UI32 size, AP4_ByteStream& stream) :
    AP4_Atom(AP4_ATOM_TYPE_DATA, size)
{
    if (size < AP4_ATOM_HEADER_SIZE+8) return;

    AP4_UI32 i;
    stream.ReadUI32(i); m_DataType = (DataType)i;
    stream.ReadUI32(i); m_DataLang = (DataLang)i;

    // the stream for the data is a substream of this source
    AP4_Position data_offset;
    stream.Tell(data_offset);
    AP4_Size data_size = size-AP4_ATOM_HEADER_SIZE-8;
    m_Source = new AP4_SubStream(stream, data_offset, data_size);
}

A NULL pointer dereference was discovered in the AP4_DataAtom class in MetaData/Ap4MetaData.cpp in Bento4 version 1.5.0-617. The vulnerability causes a segmentation fault and application crash, which leads to remote denial of service.


AP4_DataAtom::AP4_DataAtom(AP4_UI32 size, AP4_ByteStream& stream) :
    AP4_Atom(AP4_ATOM_TYPE_DATA, size),
    m_Source(NULL)
{
    if (size < AP4_ATOM_HEADER_SIZE+8) return;

    AP4_UI32 i;
    stream.ReadUI32(i); m_DataType = (DataType)i;
    stream.ReadUI32(i); m_DataLang = (DataLang)i;

    // the stream for the data is a substream of this source
    AP4_Position data_offset;
    stream.Tell(data_offset);
    AP4_Size data_size = size-AP4_ATOM_HEADER_SIZE-8;
    m_Source = new AP4_SubStream(stream, data_offset, data_size);
}


AP4_DataAtom::AP4_DataAtom(const AP4_MetaData::Value& value) :
    AP4_Atom(AP4_ATOM_TYPE_DATA, AP4_ATOM_HEADER_SIZE),
    m_DataType(DATA_TYPE_BINARY)
{
    AP4_MemoryByteStream* memory = new AP4_MemoryByteStream();
    AP4_Size payload_size = 8;
    m_Source = memory;

    switch (value.GetType()) {
        case AP4_MetaData::Value::TYPE_STRING_UTF_8: {
            m_DataType = DATA_TYPE_STRING_UTF_8;
            AP4_String string_value = value.ToString();
            if (string_value.GetLength()) {
                memory->Write(string_value.GetChars(), string_value.GetLength());
            }
            payload_size += string_value.GetLength();
            break;
        }

        case AP4_MetaData::Value::TYPE_INT_08_BE: {
            m_DataType = DATA_TYPE_SIGNED_INT_BE;
            AP4_UI08 int_value = (AP4_UI08)value.ToInteger();
            memory->Write(&int_value, 1);
            payload_size += 1;
            break;
        }

        case AP4_MetaData::Value::TYPE_INT_16_BE: {
            m_DataType = DATA_TYPE_SIGNED_INT_BE;
            AP4_UI16 int_value = (AP4_UI16)value.ToInteger();
            memory->Write(&int_value, 2);
            payload_size += 2;
            break;
        }

        case AP4_MetaData::Value::TYPE_INT_32_BE: {
            m_DataType = DATA_TYPE_SIGNED_INT_BE;
            AP4_UI32 int_value = (AP4_UI32)value.ToInteger();
            memory->Write(&int_value, 4);
            payload_size += 4;
            break;
        }

        case AP4_MetaData::Value::TYPE_JPEG:
            m_DataType = DATA_TYPE_JPEG;
            // FALLTHROUGH
        case AP4_MetaData::Value::TYPE_GIF:
            if (m_DataType == DATA_TYPE_BINARY) m_DataType = DATA_TYPE_GIF;
            // FALLTHROUGH
        case AP4_MetaData::Value::TYPE_BINARY: {
            AP4_DataBuffer buffer;
            value.ToBytes(buffer);
            if (buffer.GetDataSize()) {
                memory->Write(buffer.GetData(), buffer.GetDataSize());
            }
            payload_size += buffer.GetDataSize();
            break;
        }

        default:
            break;
    }

    const AP4_String& language = value.GetLanguage();
    if (language == "en") {
        m_DataLang = LANGUAGE_ENGLISH;
    } else {
        // default
        m_DataLang = LANGUAGE_ENGLISH;
    }

    m_Size32 += payload_size;
}


AP4_DataAtom::AP4_DataAtom(const AP4_MetaData::Value& value) :
    AP4_Atom(AP4_ATOM_TYPE_DATA, AP4_ATOM_HEADER_SIZE),
    m_DataType(DATA_TYPE_BINARY),
    m_Source(NULL)
{
    AP4_MemoryByteStream* memory = new AP4_MemoryByteStream();
    AP4_Size payload_size = 8;
    m_Source = memory;

    switch (value.GetType()) {
        case AP4_MetaData::Value::TYPE_STRING_UTF_8: {
            m_DataType = DATA_TYPE_STRING_UTF_8;
            AP4_String string_value = value.ToString();
            if (string_value.GetLength()) {
                memory->Write(string_value.GetChars(), string_value.GetLength());
            }
            payload_size += string_value.GetLength();
            break;
        }

        case AP4_MetaData::Value::TYPE_INT_08_BE: {
            m_DataType = DATA_TYPE_SIGNED_INT_BE;
            AP4_UI08 int_value = (AP4_UI08)value.ToInteger();
            memory->Write(&int_value, 1);
            payload_size += 1;
            break;
        }

        case AP4_MetaData::Value::TYPE_INT_16_BE: {
            m_DataType = DATA_TYPE_SIGNED_INT_BE;
            AP4_UI16 int_value = (AP4_UI16)value.ToInteger();
            memory->Write(&int_value, 2);
            payload_size += 2;
            break;
        }

        case AP4_MetaData::Value::TYPE_INT_32_BE: {
            m_DataType = DATA_TYPE_SIGNED_INT_BE;
            AP4_UI32 int_value = (AP4_UI32)value.ToInteger();
            memory->Write(&int_value, 4);
            payload_size += 4;
            break;
        }

        case AP4_MetaData::Value::TYPE_JPEG:
            m_DataType = DATA_TYPE_JPEG;
            // FALLTHROUGH
        case AP4_MetaData::Value::TYPE_GIF:
            if (m_DataType == DATA_TYPE_BINARY) m_DataType = DATA_TYPE_GIF;
            // FALLTHROUGH
        case AP4_MetaData::Value::TYPE_BINARY: {
            AP4_DataBuffer buffer;
            value.ToBytes(buffer);
            if (buffer.GetDataSize()) {
                memory->Write(buffer.GetData(), buffer.GetDataSize());
            }
            payload_size += buffer.GetDataSize();
            break;
        }

        default:
            break;
    }

    const AP4_String& language = value.GetLanguage();
    if (language == "en") {
        m_DataLang = LANGUAGE_ENGLISH;
    } else {
        // default
        m_DataLang = LANGUAGE_ENGLISH;
    }

    m_Size32 += payload_size;
}


Fix buffer overflow in mb_ereg_replace

Summary:
This diff has already been landed to release and to open-source branches. We're now landing it on master.

CVE-2019-11935

Reviewed By: jjergus

Differential Revision: D18177934

fbshipit-source-id: d108a59e38c67f5f5e835febd7255307605ba62c

static Variant _php_mb_regex_ereg_replace_exec(const Variant& pattern,
                                               const String& replacement,
                                               const String& str,
                                               const String& option,
                                               OnigOptionType options) {
  const char *p;
  php_mb_regex_t *re;
  OnigSyntaxType *syntax;
  OnigRegion *regs = nullptr;
  StringBuffer out_buf;
  int i, err, eval, n;
  OnigUChar *pos;
  OnigUChar *string_lim;
  char pat_buf[2];

  const mbfl_encoding *enc;

  {
    const char *current_enc_name;
    current_enc_name = php_mb_regex_mbctype2name(MBSTRG(current_mbctype));
    if (current_enc_name == nullptr ||
      (enc = mbfl_name2encoding(current_enc_name)) == nullptr) {
      raise_warning("Unknown error");
      return false;
    }
  }
  eval = 0;
  {
    if (!option.empty()) {
      _php_mb_regex_init_options(option.data(), option.size(),
                                 &options, &syntax, &eval);
    } else {
      options |= MBSTRG(regex_default_options);
      syntax = MBSTRG(regex_default_syntax);
    }
  }

  String spattern;
  if (pattern.isString()) {
    spattern = pattern.toString();
  } else {
    /* FIXME: this code is not multibyte aware! */
    pat_buf[0] = pattern.toByte();
    pat_buf[1] = '\0';
    spattern = String(pat_buf, 1, CopyString);
  }
  /* create regex pattern buffer */
  re = php_mbregex_compile_pattern(spattern, options,
                                   MBSTRG(current_mbctype), syntax);
  if (re == nullptr) {
    return false;
  }

  if (eval) {
    throw_not_supported("ereg_replace", "dynamic coding");
  }

  /* do the actual work */
  err = 0;
  pos = (OnigUChar*)str.data();
  string_lim = (OnigUChar*)(str.data() + str.size());
  regs = onig_region_new();
  while (err >= 0) {
    err = onig_search(re, (OnigUChar *)str.data(), (OnigUChar *)string_lim,
                      pos, (OnigUChar *)string_lim, regs, 0);
    if (err <= -2) {
      OnigUChar err_str[ONIG_MAX_ERROR_MESSAGE_LEN];
      onig_error_code_to_str(err_str, err);
      raise_warning("mbregex search failure: %s", err_str);
      break;
    }
    if (err >= 0) {
#if moriyoshi_0
      if (regs->beg[0] == regs->end[0]) {
        raise_warning("Empty regular expression");
        break;
      }
#endif
      /* copy the part of the string before the match */
      out_buf.append((const char *)pos,
                     (OnigUChar *)(str.data() + regs->beg[0]) - pos);
      /* copy replacement and backrefs */
      i = 0;
      p = replacement.data();
      while (i < replacement.size()) {
        int fwd = (int)php_mb_mbchar_bytes_ex(p, enc);
        n = -1;
        if ((replacement.size() - i) >= 2 && fwd == 1 &&
          p[0] == '\\' && p[1] >= '0' && p[1] <= '9') {
          n = p[1] - '0';
        }
        if (n >= 0 && n < regs->num_regs) {
          if (regs->beg[n] >= 0 && regs->beg[n] < regs->end[n] &&
              regs->end[n] <= str.size()) {
            out_buf.append(str.data() + regs->beg[n],
                           regs->end[n] - regs->beg[n]);
          }
          p += 2;
          i += 2;
        } else {
          out_buf.append(p, fwd);
          p += fwd;
          i += fwd;
        }
      }
      n = regs->end[0];
      if ((pos - (OnigUChar *)str.data()) < n) {
        pos = (OnigUChar *)(str.data() + n);
      } else {
        if (pos < string_lim) {
          out_buf.append((const char *)pos, 1);
        }
        pos++;
      }
    } else { /* nomatch */
      /* stick that last bit of string on our output */
      if (string_lim - pos > 0) {
        out_buf.append((const char *)pos, string_lim - pos);
      }
    }
    onig_region_free(regs, 0);
  }

  if (regs != nullptr) {
    onig_region_free(regs, 1);
  }

  if (err <= -2) {
    return false;
  }
  return out_buf.detach();
}

Insufficient boundary checks when processing a string in mb_ereg_replace allows access to out-of-bounds memory. This issue affects HHVM versions prior to 3.30.12, all versions between 4.0.0 and 4.8.5, all versions between 4.9.0 and 4.23.1, as well as 4.24.0, 4.25.0, 4.26.0, 4.27.0, 4.28.0, and 4.28.1.


static Variant _php_mb_regex_ereg_replace_exec(const Variant& pattern,
                                               const String& replacement,
                                               const String& str,
                                               const String& option,
                                               OnigOptionType options) {
  const char *p;
  php_mb_regex_t *re;
  OnigSyntaxType *syntax;
  OnigRegion *regs = nullptr;
  StringBuffer out_buf;
  int i, err, eval, n;
  OnigUChar *pos;
  OnigUChar *string_lim;
  char pat_buf[2];

  const mbfl_encoding *enc;

  {
    const char *current_enc_name;
    current_enc_name = php_mb_regex_mbctype2name(MBSTRG(current_mbctype));
    if (current_enc_name == nullptr ||
      (enc = mbfl_name2encoding(current_enc_name)) == nullptr) {
      raise_warning("Unknown error");
      return false;
    }
  }
  eval = 0;
  {
    if (!option.empty()) {
      _php_mb_regex_init_options(option.data(), option.size(),
                                 &options, &syntax, &eval);
    } else {
      options |= MBSTRG(regex_default_options);
      syntax = MBSTRG(regex_default_syntax);
    }
  }

  String spattern;
  if (pattern.isString()) {
    spattern = pattern.toString();
  } else {
    /* FIXME: this code is not multibyte aware! */
    pat_buf[0] = pattern.toByte();
    pat_buf[1] = '\0';
    spattern = String(pat_buf, 1, CopyString);
  }
  /* create regex pattern buffer */
  re = php_mbregex_compile_pattern(spattern, options,
                                   MBSTRG(current_mbctype), syntax);
  if (re == nullptr) {
    return false;
  }

  if (eval) {
    throw_not_supported("ereg_replace", "dynamic coding");
  }

  /* do the actual work */
  err = 0;
  pos = (OnigUChar*)str.data();
  string_lim = (OnigUChar*)(str.data() + str.size());
  regs = onig_region_new();
  while (err >= 0) {
    err = onig_search(re, (OnigUChar *)str.data(), (OnigUChar *)string_lim,
                      pos, (OnigUChar *)string_lim, regs, 0);
    if (err <= -2) {
      OnigUChar err_str[ONIG_MAX_ERROR_MESSAGE_LEN];
      onig_error_code_to_str(err_str, err);
      raise_warning("mbregex search failure: %s", err_str);
      break;
    }
    if (err >= 0) {
#if moriyoshi_0
      if (regs->beg[0] == regs->end[0]) {
        raise_warning("Empty regular expression");
        break;
      }
#endif
      /* copy the part of the string before the match */
      out_buf.append((const char *)pos,
                     (OnigUChar *)(str.data() + regs->beg[0]) - pos);
      /* copy replacement and backrefs */
      i = 0;
      p = replacement.data();
      while (i < replacement.size()) {
        int fwd = (int)php_mb_mbchar_bytes_ex(p, enc);
        n = -1;
        auto const remaining = replacement.size() - i;
        if (remaining >= 2 && fwd == 1 &&
            p[0] == '\\' && p[1] >= '0' && p[1] <= '9') {
          n = p[1] - '0';
        }
        if (n >= 0 && n < regs->num_regs) {
          if (regs->beg[n] >= 0 && regs->beg[n] < regs->end[n] &&
              regs->end[n] <= str.size()) {
            out_buf.append(str.data() + regs->beg[n],
                           regs->end[n] - regs->beg[n]);
          }
          p += 2;
          i += 2;
        } else if (remaining >= fwd) {
          out_buf.append(p, fwd);
          p += fwd;
          i += fwd;
        } else {
          raise_warning("Replacement ends with unterminated %s: 0x%hhx",
                        enc->name, *p);
          break;
        }
      }
      n = regs->end[0];
      if ((pos - (OnigUChar *)str.data()) < n) {
        pos = (OnigUChar *)(str.data() + n);
      } else {
        if (pos < string_lim) {
          out_buf.append((const char *)pos, 1);
        }
        pos++;
      }
    } else { /* nomatch */
      /* stick that last bit of string on our output */
      if (string_lim - pos > 0) {
        out_buf.append((const char *)pos, string_lim - pos);
      }
    }
    onig_region_free(regs, 0);
  }

  if (regs != nullptr) {
    onig_region_free(regs, 1);
  }

  if (err <= -2) {
    return false;
  }
  return out_buf.detach();
}


AP4_AtomFactory::CreateAtomFromStream(AP4_ByteStream& stream,
                                      AP4_UI32        type,
                                      AP4_UI32        size_32,
                                      AP4_UI64        size_64,
                                      AP4_Atom*&      atom)
{
    bool atom_is_large = (size_32 == 1);
    bool force_64 = (size_32==1 && ((size_64>>32) == 0));

    // create the atom
    if (GetContext() == AP4_ATOM_TYPE_STSD) {
        // sample entry
        if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
        switch (type) {
          case AP4_ATOM_TYPE_MP4A:
            atom = new AP4_Mp4aSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_MP4V:
            atom = new AP4_Mp4vSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_MP4S:
            atom = new AP4_Mp4sSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ENCA:
            atom = new AP4_EncaSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ENCV:
            atom = new AP4_EncvSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_DRMS:
            atom = new AP4_DrmsSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_DRMI:
            atom = new AP4_DrmiSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_AVC1:
          case AP4_ATOM_TYPE_AVC2:
          case AP4_ATOM_TYPE_AVC3:
          case AP4_ATOM_TYPE_AVC4:
          case AP4_ATOM_TYPE_DVAV:
          case AP4_ATOM_TYPE_DVA1:
            atom = new AP4_AvcSampleEntry(type, size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_HEV1:
          case AP4_ATOM_TYPE_HVC1:
          case AP4_ATOM_TYPE_DVHE:
          case AP4_ATOM_TYPE_DVH1:
            atom = new AP4_HevcSampleEntry(type, size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ALAC:
          case AP4_ATOM_TYPE_AC_3:
          case AP4_ATOM_TYPE_EC_3:
          case AP4_ATOM_TYPE_DTSC:
          case AP4_ATOM_TYPE_DTSH:
          case AP4_ATOM_TYPE_DTSL:
          case AP4_ATOM_TYPE_DTSE:
            atom = new AP4_AudioSampleEntry(type, size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_RTP_:
            atom = new AP4_RtpHintSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_STPP:
            atom = new AP4_SubtitleSampleEntry(type, size_32, stream, *this);
            break;

          default: {
            // try all the external type handlers
            AP4_List<TypeHandler>::Item* handler_item = m_TypeHandlers.FirstItem();
            while (handler_item) {
                TypeHandler* handler = handler_item->GetData();
                if (AP4_SUCCEEDED(handler->CreateAtom(type, size_32, stream, GetContext(), atom))) {
                    break;
                }
                handler_item = handler_item->GetNext();
            }

            // no custom handler, create a generic entry
            if (atom == NULL) {
                atom = new AP4_UnknownSampleEntry(type, (AP4_UI32)size_64, stream);
            }

            break;
          }
        }
    } else {
        // regular atom
        switch (type) {
          case AP4_ATOM_TYPE_MOOV:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MoovAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_MVHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MvhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MEHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MehdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MFHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MfhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TRAK:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TrakAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_TREX:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TrexAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HDLR:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HdlrAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TKHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TkhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TFHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TfhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TRUN:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TrunAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TFRA:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TfraAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MFRO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MfroAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MDHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MdhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STSD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StsdAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_STSC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StscAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STCO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StcoAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_CO64:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_Co64Atom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STSZ:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StszAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STZ2:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_Stz2Atom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STTS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SttsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_CTTS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_CttsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STSS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StssAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_IODS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IodsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ESDS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_EsdsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_AVCC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_AvccAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HVCC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HvccAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_DVCC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_DvccAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HVCE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HvccAtom::Create(size_32, stream);
            atom->SetType(AP4_ATOM_TYPE_HVCE);
            break;

          case AP4_ATOM_TYPE_AVCE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_AvccAtom::Create(size_32, stream);
            atom->SetType(AP4_ATOM_TYPE_AVCE);
            break;

    #if !defined(AP4_CONFIG_MINI_BUILD)
          case AP4_ATOM_TYPE_UUID: {
              AP4_UI08 uuid[16];
              AP4_Result result = stream.Read(uuid, 16);
              if (AP4_FAILED(result)) return result;

              if (AP4_CompareMemory(uuid, AP4_UUID_PIFF_TRACK_ENCRYPTION_ATOM, 16) == 0) {
                  atom = AP4_PiffTrackEncryptionAtom::Create((AP4_UI32)size_64, stream);
              } else if (AP4_CompareMemory(uuid, AP4_UUID_PIFF_SAMPLE_ENCRYPTION_ATOM, 16) == 0) {
                  atom = AP4_PiffSampleEncryptionAtom::Create((AP4_UI32)size_64, stream);
              } else {
                  atom = new AP4_UnknownUuidAtom(size_64, uuid, stream);
              }
              break;
          }

          case AP4_ATOM_TYPE_8ID_:
            atom = new AP4_NullTerminatedStringAtom(type, size_64, stream);
            break;

          case AP4_ATOM_TYPE_8BDL:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_8bdlAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_DREF:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_DrefAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_URL:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_UrlAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ELST:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_ElstAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_VMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_VmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_NMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_NmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SthdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_FRMA:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_FrmaAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SCHM:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SchmAtom::Create(size_32, &m_ContextStack, stream);
            break;

          case AP4_ATOM_TYPE_FTYP:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_FtypAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TIMS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TimsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SDP_:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SdpAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_IKMS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IkmsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ISFM:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IsfmAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ISLT:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IsltAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ODHE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_OdheAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_OHDR:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_OhdrAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ODDA:
            atom = AP4_OddaAtom::Create(size_64, stream);
            break;

          case AP4_ATOM_TYPE_ODAF:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_OdafAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_GRPI:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_GrpiAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_IPRO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IproAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_RTP_:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_RtpAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TFDT:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TfdtAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TENC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TencAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SENC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SencAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SAIZ:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SaizAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SAIO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SaioAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_PDIN:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_PdinAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_BLOC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_BlocAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_AINF:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_AinfAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_PSSH:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_PsshAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SIDX:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SidxAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SBGP:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SbgpAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SGPD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SgpdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MKID:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            if (GetContext() == AP4_ATOM_TYPE_MARL) {
                atom = AP4_MkidAtom::Create(size_32, stream);
            }
            break;

          case AP4_ATOM_TYPE_DEC3:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            if (GetContext() == AP4_ATOM_TYPE_EC_3 || GetContext() == AP4_ATOM_TYPE_ENCA) {
                atom = AP4_Dec3Atom::Create(size_32, stream);
            }
            break;

          // track ref types
          case AP4_ATOM_TYPE_HINT:
          case AP4_ATOM_TYPE_CDSC:
          case AP4_ATOM_TYPE_SYNC:
          case AP4_ATOM_TYPE_MPOD:
          case AP4_ATOM_TYPE_DPND:
          case AP4_ATOM_TYPE_IPIR:
          case AP4_ATOM_TYPE_ALIS:
          case AP4_ATOM_TYPE_CHAP:
            if (GetContext() == AP4_ATOM_TYPE_TREF) {
                if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
                atom = AP4_TrefTypeAtom::Create(type, size_32, stream);
            }
            break;

    #endif // AP4_CONFIG_MINI_BUILD

          // container atoms
          case AP4_ATOM_TYPE_MOOF:
          case AP4_ATOM_TYPE_MVEX:
          case AP4_ATOM_TYPE_TRAF:
          case AP4_ATOM_TYPE_TREF:
          case AP4_ATOM_TYPE_MFRA:
          case AP4_ATOM_TYPE_HNTI:
          case AP4_ATOM_TYPE_STBL:
          case AP4_ATOM_TYPE_MDIA:
          case AP4_ATOM_TYPE_DINF:
          case AP4_ATOM_TYPE_MINF:
          case AP4_ATOM_TYPE_SCHI:
          case AP4_ATOM_TYPE_SINF:
          case AP4_ATOM_TYPE_UDTA:
          case AP4_ATOM_TYPE_ILST:
          case AP4_ATOM_TYPE_EDTS:
          case AP4_ATOM_TYPE_MDRI:
          case AP4_ATOM_TYPE_WAVE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_ContainerAtom::Create(type, size_64, false, force_64, stream, *this);
            break;

          // containers, only at the top
          case AP4_ATOM_TYPE_MARL:
            if (GetContext() == 0) {
                atom = AP4_ContainerAtom::Create(type, size_64, false, force_64, stream, *this);
            }
            break;

          // full container atoms
          case AP4_ATOM_TYPE_META:
          case AP4_ATOM_TYPE_ODRM:
          case AP4_ATOM_TYPE_ODKM:
            atom = AP4_ContainerAtom::Create(type, size_64, true, force_64, stream, *this);
            break;

          case AP4_ATOM_TYPE_FREE:
          case AP4_ATOM_TYPE_WIDE:
          case AP4_ATOM_TYPE_MDAT:
            // generic atoms
            break;

          default: {
            // try all the external type handlers
            AP4_List<TypeHandler>::Item* handler_item = m_TypeHandlers.FirstItem();
            while (handler_item) {
                TypeHandler* handler = handler_item->GetData();
                if (AP4_SUCCEEDED(handler->CreateAtom(type, size_32, stream, GetContext(), atom))) {
                    break;
                }
                handler_item = handler_item->GetNext();
            }

            break;
          }
        }
    }

    return AP4_SUCCESS;
}

AP4_AtomFactory::CreateAtomFromStream in Core/Ap4AtomFactory.cpp in Bento4 version 1.5.0-617 has missing NULL checks, leading to a NULL pointer dereference, segmentation fault, and application crash in AP4_Atom::SetType in Core/Ap4Atom.h.


AP4_AtomFactory::CreateAtomFromStream(AP4_ByteStream& stream,
                                      AP4_UI32        type,
                                      AP4_UI32        size_32,
                                      AP4_UI64        size_64,
                                      AP4_Atom*&      atom)
{
    bool atom_is_large = (size_32 == 1);
    bool force_64 = (size_32==1 && ((size_64>>32) == 0));

    // create the atom
    if (GetContext() == AP4_ATOM_TYPE_STSD) {
        // sample entry
        if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
        switch (type) {
          case AP4_ATOM_TYPE_MP4A:
            atom = new AP4_Mp4aSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_MP4V:
            atom = new AP4_Mp4vSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_MP4S:
            atom = new AP4_Mp4sSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ENCA:
            atom = new AP4_EncaSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ENCV:
            atom = new AP4_EncvSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_DRMS:
            atom = new AP4_DrmsSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_DRMI:
            atom = new AP4_DrmiSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_AVC1:
          case AP4_ATOM_TYPE_AVC2:
          case AP4_ATOM_TYPE_AVC3:
          case AP4_ATOM_TYPE_AVC4:
          case AP4_ATOM_TYPE_DVAV:
          case AP4_ATOM_TYPE_DVA1:
            atom = new AP4_AvcSampleEntry(type, size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_HEV1:
          case AP4_ATOM_TYPE_HVC1:
          case AP4_ATOM_TYPE_DVHE:
          case AP4_ATOM_TYPE_DVH1:
            atom = new AP4_HevcSampleEntry(type, size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ALAC:
          case AP4_ATOM_TYPE_AC_3:
          case AP4_ATOM_TYPE_EC_3:
          case AP4_ATOM_TYPE_DTSC:
          case AP4_ATOM_TYPE_DTSH:
          case AP4_ATOM_TYPE_DTSL:
          case AP4_ATOM_TYPE_DTSE:
            atom = new AP4_AudioSampleEntry(type, size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_RTP_:
            atom = new AP4_RtpHintSampleEntry(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_STPP:
            atom = new AP4_SubtitleSampleEntry(type, size_32, stream, *this);
            break;

          default: {
            // try all the external type handlers
            AP4_List<TypeHandler>::Item* handler_item = m_TypeHandlers.FirstItem();
            while (handler_item) {
                TypeHandler* handler = handler_item->GetData();
                if (AP4_SUCCEEDED(handler->CreateAtom(type, size_32, stream, GetContext(), atom))) {
                    break;
                }
                handler_item = handler_item->GetNext();
            }

            // no custom handler, create a generic entry
            if (atom == NULL) {
                atom = new AP4_UnknownSampleEntry(type, (AP4_UI32)size_64, stream);
            }

            break;
          }
        }
    } else {
        // regular atom
        switch (type) {
          case AP4_ATOM_TYPE_MOOV:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MoovAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_MVHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MvhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MEHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MehdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MFHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MfhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TRAK:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TrakAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_TREX:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TrexAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HDLR:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HdlrAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TKHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TkhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TFHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TfhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TRUN:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TrunAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TFRA:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TfraAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MFRO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MfroAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MDHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_MdhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STSD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StsdAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_STSC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StscAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STCO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StcoAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_CO64:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_Co64Atom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STSZ:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StszAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STZ2:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_Stz2Atom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STTS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SttsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_CTTS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_CttsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STSS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_StssAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_IODS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IodsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ESDS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_EsdsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_AVCC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_AvccAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HVCC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HvccAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_DVCC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_DvccAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HVCE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HvccAtom::Create(size_32, stream);
            if (atom) {
                atom->SetType(AP4_ATOM_TYPE_HVCE);
            }
            break;

          case AP4_ATOM_TYPE_AVCE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_AvccAtom::Create(size_32, stream);
            if (atom) {
                atom->SetType(AP4_ATOM_TYPE_AVCE);
            }
            break;

    #if !defined(AP4_CONFIG_MINI_BUILD)
          case AP4_ATOM_TYPE_UUID: {
              AP4_UI08 uuid[16];
              AP4_Result result = stream.Read(uuid, 16);
              if (AP4_FAILED(result)) return result;

              if (AP4_CompareMemory(uuid, AP4_UUID_PIFF_TRACK_ENCRYPTION_ATOM, 16) == 0) {
                  atom = AP4_PiffTrackEncryptionAtom::Create((AP4_UI32)size_64, stream);
              } else if (AP4_CompareMemory(uuid, AP4_UUID_PIFF_SAMPLE_ENCRYPTION_ATOM, 16) == 0) {
                  atom = AP4_PiffSampleEncryptionAtom::Create((AP4_UI32)size_64, stream);
              } else {
                  atom = new AP4_UnknownUuidAtom(size_64, uuid, stream);
              }
              break;
          }

          case AP4_ATOM_TYPE_8ID_:
            atom = new AP4_NullTerminatedStringAtom(type, size_64, stream);
            break;

          case AP4_ATOM_TYPE_8BDL:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_8bdlAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_DREF:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_DrefAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_URL:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_UrlAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ELST:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_ElstAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_VMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_VmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_NMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_NmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_STHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SthdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_HMHD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_HmhdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_FRMA:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_FrmaAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SCHM:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SchmAtom::Create(size_32, &m_ContextStack, stream);
            break;

          case AP4_ATOM_TYPE_FTYP:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_FtypAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TIMS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TimsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SDP_:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SdpAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_IKMS:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IkmsAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ISFM:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IsfmAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ISLT:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IsltAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_ODHE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_OdheAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_OHDR:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_OhdrAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_ODDA:
            atom = AP4_OddaAtom::Create(size_64, stream);
            break;

          case AP4_ATOM_TYPE_ODAF:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_OdafAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_GRPI:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_GrpiAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_IPRO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_IproAtom::Create(size_32, stream, *this);
            break;

          case AP4_ATOM_TYPE_RTP_:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_RtpAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TFDT:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TfdtAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_TENC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_TencAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SENC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SencAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SAIZ:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SaizAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SAIO:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SaioAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_PDIN:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_PdinAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_BLOC:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_BlocAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_AINF:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_AinfAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_PSSH:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_PsshAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SIDX:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SidxAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SBGP:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SbgpAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_SGPD:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_SgpdAtom::Create(size_32, stream);
            break;

          case AP4_ATOM_TYPE_MKID:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            if (GetContext() == AP4_ATOM_TYPE_MARL) {
                atom = AP4_MkidAtom::Create(size_32, stream);
            }
            break;

          case AP4_ATOM_TYPE_DEC3:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            if (GetContext() == AP4_ATOM_TYPE_EC_3 || GetContext() == AP4_ATOM_TYPE_ENCA) {
                atom = AP4_Dec3Atom::Create(size_32, stream);
            }
            break;

          // track ref types
          case AP4_ATOM_TYPE_HINT:
          case AP4_ATOM_TYPE_CDSC:
          case AP4_ATOM_TYPE_SYNC:
          case AP4_ATOM_TYPE_MPOD:
          case AP4_ATOM_TYPE_DPND:
          case AP4_ATOM_TYPE_IPIR:
          case AP4_ATOM_TYPE_ALIS:
          case AP4_ATOM_TYPE_CHAP:
            if (GetContext() == AP4_ATOM_TYPE_TREF) {
                if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
                atom = AP4_TrefTypeAtom::Create(type, size_32, stream);
            }
            break;

    #endif // AP4_CONFIG_MINI_BUILD

          // container atoms
          case AP4_ATOM_TYPE_MOOF:
          case AP4_ATOM_TYPE_MVEX:
          case AP4_ATOM_TYPE_TRAF:
          case AP4_ATOM_TYPE_TREF:
          case AP4_ATOM_TYPE_MFRA:
          case AP4_ATOM_TYPE_HNTI:
          case AP4_ATOM_TYPE_STBL:
          case AP4_ATOM_TYPE_MDIA:
          case AP4_ATOM_TYPE_DINF:
          case AP4_ATOM_TYPE_MINF:
          case AP4_ATOM_TYPE_SCHI:
          case AP4_ATOM_TYPE_SINF:
          case AP4_ATOM_TYPE_UDTA:
          case AP4_ATOM_TYPE_ILST:
          case AP4_ATOM_TYPE_EDTS:
          case AP4_ATOM_TYPE_MDRI:
          case AP4_ATOM_TYPE_WAVE:
            if (atom_is_large) return AP4_ERROR_INVALID_FORMAT;
            atom = AP4_ContainerAtom::Create(type, size_64, false, force_64, stream, *this);
            break;

          // containers, only at the top
          case AP4_ATOM_TYPE_MARL:
            if (GetContext() == 0) {
                atom = AP4_ContainerAtom::Create(type, size_64, false, force_64, stream, *this);
            }
            break;

          // full container atoms
          case AP4_ATOM_TYPE_META:
          case AP4_ATOM_TYPE_ODRM:
          case AP4_ATOM_TYPE_ODKM:
            atom = AP4_ContainerAtom::Create(type, size_64, true, force_64, stream, *this);
            break;

          case AP4_ATOM_TYPE_FREE:
          case AP4_ATOM_TYPE_WIDE:
          case AP4_ATOM_TYPE_MDAT:
            // generic atoms
            break;

          default: {
            // try all the external type handlers
            AP4_List<TypeHandler>::Item* handler_item = m_TypeHandlers.FirstItem();
            while (handler_item) {
                TypeHandler* handler = handler_item->GetData();
                if (AP4_SUCCEEDED(handler->CreateAtom(type, size_32, stream, GetContext(), atom))) {
                    break;
                }
                handler_item = handler_item->GetNext();
            }

            break;
          }
        }
    }

    return AP4_SUCCESS;
}


CVE-2016-1000109: Ignore Proxy HTTP header from fastcgi requests

Summary:
The default PHP behavior is to transform a Proxy header from requests
into the HTTP_PROXY environment variable in the PHP environment running the
request. Some scripts may use this env var to decide which HTTP proxy to
connect to, and we don't want requests to have that kind of control over
scripts' behavior.

Reviewed By: alexmalyshev, Orvid

Differential Revision: D3579221

fbshipit-source-id: 18a405fd73aee65283aea99ff995f8082b3fc3b5

void FastCGITransport::onHeader(std::unique_ptr<folly::IOBuf> key_chain,
                                std::unique_ptr<folly::IOBuf> value_chain) {
  Cursor keyCur(key_chain.get());
  auto key = keyCur.readFixedString(key_chain->computeChainDataLength());

  Cursor valCur(value_chain.get());
  auto value = valCur.readFixedString(value_chain->computeChainDataLength());

  m_requestParams[key] = value;
}

HHVM does not attempt to address RFC 3875 section 4.1.18 namespace conflicts and therefore does not protect CGI applications from the presence of untrusted client data in the HTTP_PROXY environment variable, which might allow remote attackers to redirect a CGI application's outbound HTTP traffic to an arbitrary proxy server via a crafted Proxy header in an HTTP request, aka an "httpoxy" issue. This issue affects HHVM versions prior to 3.9.6, all versions between 3.10.0 and 3.12.4 (inclusive), and all versions between 3.13.0 and 3.14.2 (inclusive).


void FastCGITransport::onHeader(std::unique_ptr<folly::IOBuf> key_chain,
                                std::unique_ptr<folly::IOBuf> value_chain) {
  Cursor keyCur(key_chain.get());
  auto key = keyCur.readFixedString(key_chain->computeChainDataLength());

  // Don't allow requests to inject an HTTP_PROXY environment variable by
  // sending a Proxy header.
  if (strcasecmp(key.c_str(), "HTTP_PROXY") == 0) return;

  Cursor valCur(value_chain.get());
  auto value = valCur.readFixedString(value_chain->computeChainDataLength());

  m_requestParams[key] = value;
}


Fix integer overflow in StringUtil::implode

Reviewed By: ricklavoie

Differential Revision: D3623922

fbshipit-source-id: 136d124a850c07cc6c63535afc11d36499d576fc

String StringUtil::Implode(const Variant& items, const String& delim,
                           const bool checkIsContainer /* = true */) {
  if (checkIsContainer && !isContainer(items)) {
    throw_param_is_not_container();
  }
  int size = getContainerSize(items);
  if (size == 0) return empty_string();

  req::vector<String> sitems;
  sitems.reserve(size);
  int len = 0;
  int lenDelim = delim.size();
  for (ArrayIter iter(items); iter; ++iter) {
    sitems.emplace_back(iter.second().toString());
    len += sitems.back().size() + lenDelim;
  }
  len -= lenDelim; // always one delimiter less than count of items
  assert(sitems.size() == size);

  String s = String(len, ReserveString);
  char *buffer = s.mutableData();
  const char *sdelim = delim.data();
  char *p = buffer;
  String &init_str = sitems[0];
  int init_len = init_str.size();
  memcpy(p, init_str.data(), init_len);
  p += init_len;
  for (int i = 1; i < size; i++) {
    String &item = sitems[i];
    memcpy(p, sdelim, lenDelim);
    p += lenDelim;
    int lenItem = item.size();
    memcpy(p, item.data(), lenItem);
    p += lenItem;
  }
  assert(p - buffer == len);
  s.setSize(len);
  return s;
}

Integer overflow in StringUtil::implode in Facebook HHVM before 3.15.0 allows attackers to have unspecified impact via unknown vectors.


String StringUtil::Implode(const Variant& items, const String& delim,
                           const bool checkIsContainer /* = true */) {
  if (checkIsContainer && !isContainer(items)) {
    throw_param_is_not_container();
  }
  int size = getContainerSize(items);
  if (size == 0) return empty_string();

  req::vector<String> sitems;
  sitems.reserve(size);
  size_t len = 0;
  size_t lenDelim = delim.size();
  for (ArrayIter iter(items); iter; ++iter) {
    sitems.emplace_back(iter.second().toString());
    len += sitems.back().size() + lenDelim;
  }
  len -= lenDelim; // always one delimiter less than count of items
  assert(sitems.size() == size);

  String s = String(len, ReserveString);
  char *buffer = s.mutableData();
  const char *sdelim = delim.data();
  char *p = buffer;
  String &init_str = sitems[0];
  int init_len = init_str.size();
  memcpy(p, init_str.data(), init_len);
  p += init_len;
  for (int i = 1; i < size; i++) {
    String &item = sitems[i];
    memcpy(p, sdelim, lenDelim);
    p += lenDelim;
    int lenItem = item.size();
    memcpy(p, item.data(), lenItem);
    p += lenItem;
  }
  assert(p - buffer == len);
  s.setSize(len);
  return s;
}


Received SACK options are managed by a linked list at the TCP socket.
There is a global tunable limit net.inet.tcp.sackholelimit, default
is 32768.  If an attacker manages to attach all these sack holes
to a few TCP connections, the lists may grow long.  Traversing them
might cause higher CPU consumption on the victim machine.  In
practice such a situation is hard to create as the TCP retransmit
and 2*msl timer flush the list periodically.  For additional
protection, enforce a per connection limit of 128 SACK holes in the
list.
reported by Reuven Plevinsky and Tal Vainshtein
discussed with claudio@ and procter@; OK deraadt@

tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
{
	int tmp_olen;
	u_char *tmp_cp;
	struct sackhole *cur, *p, *temp;

	if (!tp->sack_enable)
		return;
	/* SACK without ACK doesn't make sense. */
	if ((th->th_flags & TH_ACK) == 0)
	       return;
	/* Make sure the ACK on this segment is in [snd_una, snd_max]. */
	if (SEQ_LT(th->th_ack, tp->snd_una) ||
	    SEQ_GT(th->th_ack, tp->snd_max))
		return;
	/* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */
	if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0)
		return;
	/* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */
	tmp_cp = cp + 2;
	tmp_olen = optlen - 2;
	tcpstat_inc(tcps_sack_rcv_opts);
	if (tp->snd_numholes < 0)
		tp->snd_numholes = 0;
	if (tp->t_maxseg == 0)
		panic("tcp_sack_option"); /* Should never happen */
	while (tmp_olen > 0) {
		struct sackblk sack;

		memcpy(&sack.start, tmp_cp, sizeof(tcp_seq));
		sack.start = ntohl(sack.start);
		memcpy(&sack.end, tmp_cp + sizeof(tcp_seq), sizeof(tcp_seq));
		sack.end = ntohl(sack.end);
		tmp_olen -= TCPOLEN_SACK;
		tmp_cp += TCPOLEN_SACK;
		if (SEQ_LEQ(sack.end, sack.start))
			continue; /* bad SACK fields */
		if (SEQ_LEQ(sack.end, tp->snd_una))
			continue; /* old block */
		if (SEQ_GT(th->th_ack, tp->snd_una)) {
			if (SEQ_LT(sack.start, th->th_ack))
				continue;
		}
		if (SEQ_GT(sack.end, tp->snd_max))
			continue;
		if (tp->snd_holes == NULL) { /* first hole */
			tp->snd_holes = (struct sackhole *)
			    pool_get(&sackhl_pool, PR_NOWAIT);
			if (tp->snd_holes == NULL) {
				/* ENOBUFS, so ignore SACKed block for now*/
				goto done;
			}
			cur = tp->snd_holes;
			cur->start = th->th_ack;
			cur->end = sack.start;
			cur->rxmit = cur->start;
			cur->next = NULL;
			tp->snd_numholes = 1;
			tp->rcv_lastsack = sack.end;
			/*
			 * dups is at least one.  If more data has been
			 * SACKed, it can be greater than one.
			 */
			cur->dups = min(tcprexmtthresh,
			    ((sack.end - cur->end)/tp->t_maxseg));
			if (cur->dups < 1)
				cur->dups = 1;
			continue; /* with next sack block */
		}
		/* Go thru list of holes:  p = previous,  cur = current */
		p = cur = tp->snd_holes;
		while (cur) {
			if (SEQ_LEQ(sack.end, cur->start))
				/* SACKs data before the current hole */
				break; /* no use going through more holes */
			if (SEQ_GEQ(sack.start, cur->end)) {
				/* SACKs data beyond the current hole */
				cur->dups++;
				if (((sack.end - cur->end)/tp->t_maxseg) >=
				    tcprexmtthresh)
					cur->dups = tcprexmtthresh;
				p = cur;
				cur = cur->next;
				continue;
			}
			if (SEQ_LEQ(sack.start, cur->start)) {
				/* Data acks at least the beginning of hole */
				if (SEQ_GEQ(sack.end, cur->end)) {
					/* Acks entire hole, so delete hole */
					if (p != cur) {
						p->next = cur->next;
						pool_put(&sackhl_pool, cur);
						cur = p->next;
					} else {
						cur = cur->next;
						pool_put(&sackhl_pool, p);
						p = cur;
						tp->snd_holes = p;
					}
					tp->snd_numholes--;
					continue;
				}
				/* otherwise, move start of hole forward */
				cur->start = sack.end;
				cur->rxmit = SEQ_MAX(cur->rxmit, cur->start);
				p = cur;
				cur = cur->next;
				continue;
			}
			/* move end of hole backward */
			if (SEQ_GEQ(sack.end, cur->end)) {
				cur->end = sack.start;
				cur->rxmit = SEQ_MIN(cur->rxmit, cur->end);
				cur->dups++;
				if (((sack.end - cur->end)/tp->t_maxseg) >=
				    tcprexmtthresh)
					cur->dups = tcprexmtthresh;
				p = cur;
				cur = cur->next;
				continue;
			}
			if (SEQ_LT(cur->start, sack.start) &&
			    SEQ_GT(cur->end, sack.end)) {
				/*
				 * ACKs some data in middle of a hole; need to
				 * split current hole
				 */
				temp = (struct sackhole *)
				    pool_get(&sackhl_pool, PR_NOWAIT);
				if (temp == NULL)
					goto done; /* ENOBUFS */
				temp->next = cur->next;
				temp->start = sack.end;
				temp->end = cur->end;
				temp->dups = cur->dups;
				temp->rxmit = SEQ_MAX(cur->rxmit, temp->start);
				cur->end = sack.start;
				cur->rxmit = SEQ_MIN(cur->rxmit, cur->end);
				cur->dups++;
				if (((sack.end - cur->end)/tp->t_maxseg) >=
					tcprexmtthresh)
					cur->dups = tcprexmtthresh;
				cur->next = temp;
				p = temp;
				cur = p->next;
				tp->snd_numholes++;
			}
		}
		/* At this point, p points to the last hole on the list */
		if (SEQ_LT(tp->rcv_lastsack, sack.start)) {
			/*
			 * Need to append new hole at end.
			 * Last hole is p (and it's not NULL).
			 */
			temp = (struct sackhole *)
			    pool_get(&sackhl_pool, PR_NOWAIT);
			if (temp == NULL)
				goto done; /* ENOBUFS */
			temp->start = tp->rcv_lastsack;
			temp->end = sack.start;
			temp->dups = min(tcprexmtthresh,
			    ((sack.end - sack.start)/tp->t_maxseg));
			if (temp->dups < 1)
				temp->dups = 1;
			temp->rxmit = temp->start;
			temp->next = 0;
			p->next = temp;
			tp->rcv_lastsack = sack.end;
			tp->snd_numholes++;
		}
	}
done:
	return;
}

OpenBSD kernel version <= 6.5 can be forced to create long chains of TCP SACK holes that causes very expensive calls to tcp_sack_option() for every incoming SACK packet which can lead to a denial of service.


tcp_sack_option(struct tcpcb *tp, struct tcphdr *th, u_char *cp, int optlen)
{
	int tmp_olen;
	u_char *tmp_cp;
	struct sackhole *cur, *p, *temp;

	if (!tp->sack_enable)
		return;
	/* SACK without ACK doesn't make sense. */
	if ((th->th_flags & TH_ACK) == 0)
	       return;
	/* Make sure the ACK on this segment is in [snd_una, snd_max]. */
	if (SEQ_LT(th->th_ack, tp->snd_una) ||
	    SEQ_GT(th->th_ack, tp->snd_max))
		return;
	/* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */
	if (optlen <= 2 || (optlen - 2) % TCPOLEN_SACK != 0)
		return;
	/* Note: TCPOLEN_SACK must be 2*sizeof(tcp_seq) */
	tmp_cp = cp + 2;
	tmp_olen = optlen - 2;
	tcpstat_inc(tcps_sack_rcv_opts);
	if (tp->snd_numholes < 0)
		tp->snd_numholes = 0;
	if (tp->t_maxseg == 0)
		panic("tcp_sack_option"); /* Should never happen */
	while (tmp_olen > 0) {
		struct sackblk sack;

		memcpy(&sack.start, tmp_cp, sizeof(tcp_seq));
		sack.start = ntohl(sack.start);
		memcpy(&sack.end, tmp_cp + sizeof(tcp_seq), sizeof(tcp_seq));
		sack.end = ntohl(sack.end);
		tmp_olen -= TCPOLEN_SACK;
		tmp_cp += TCPOLEN_SACK;
		if (SEQ_LEQ(sack.end, sack.start))
			continue; /* bad SACK fields */
		if (SEQ_LEQ(sack.end, tp->snd_una))
			continue; /* old block */
		if (SEQ_GT(th->th_ack, tp->snd_una)) {
			if (SEQ_LT(sack.start, th->th_ack))
				continue;
		}
		if (SEQ_GT(sack.end, tp->snd_max))
			continue;
		if (tp->snd_holes == NULL) { /* first hole */
			tp->snd_holes = (struct sackhole *)
			    pool_get(&sackhl_pool, PR_NOWAIT);
			if (tp->snd_holes == NULL) {
				/* ENOBUFS, so ignore SACKed block for now*/
				goto done;
			}
			cur = tp->snd_holes;
			cur->start = th->th_ack;
			cur->end = sack.start;
			cur->rxmit = cur->start;
			cur->next = NULL;
			tp->snd_numholes = 1;
			tp->rcv_lastsack = sack.end;
			/*
			 * dups is at least one.  If more data has been
			 * SACKed, it can be greater than one.
			 */
			cur->dups = min(tcprexmtthresh,
			    ((sack.end - cur->end)/tp->t_maxseg));
			if (cur->dups < 1)
				cur->dups = 1;
			continue; /* with next sack block */
		}
		/* Go thru list of holes:  p = previous,  cur = current */
		p = cur = tp->snd_holes;
		while (cur) {
			if (SEQ_LEQ(sack.end, cur->start))
				/* SACKs data before the current hole */
				break; /* no use going through more holes */
			if (SEQ_GEQ(sack.start, cur->end)) {
				/* SACKs data beyond the current hole */
				cur->dups++;
				if (((sack.end - cur->end)/tp->t_maxseg) >=
				    tcprexmtthresh)
					cur->dups = tcprexmtthresh;
				p = cur;
				cur = cur->next;
				continue;
			}
			if (SEQ_LEQ(sack.start, cur->start)) {
				/* Data acks at least the beginning of hole */
				if (SEQ_GEQ(sack.end, cur->end)) {
					/* Acks entire hole, so delete hole */
					if (p != cur) {
						p->next = cur->next;
						pool_put(&sackhl_pool, cur);
						cur = p->next;
					} else {
						cur = cur->next;
						pool_put(&sackhl_pool, p);
						p = cur;
						tp->snd_holes = p;
					}
					tp->snd_numholes--;
					continue;
				}
				/* otherwise, move start of hole forward */
				cur->start = sack.end;
				cur->rxmit = SEQ_MAX(cur->rxmit, cur->start);
				p = cur;
				cur = cur->next;
				continue;
			}
			/* move end of hole backward */
			if (SEQ_GEQ(sack.end, cur->end)) {
				cur->end = sack.start;
				cur->rxmit = SEQ_MIN(cur->rxmit, cur->end);
				cur->dups++;
				if (((sack.end - cur->end)/tp->t_maxseg) >=
				    tcprexmtthresh)
					cur->dups = tcprexmtthresh;
				p = cur;
				cur = cur->next;
				continue;
			}
			if (SEQ_LT(cur->start, sack.start) &&
			    SEQ_GT(cur->end, sack.end)) {
				/*
				 * ACKs some data in middle of a hole; need to
				 * split current hole
				 */
				if (tp->snd_numholes >= TCP_SACKHOLE_LIMIT)
					goto done;
				temp = (struct sackhole *)
				    pool_get(&sackhl_pool, PR_NOWAIT);
				if (temp == NULL)
					goto done; /* ENOBUFS */
				temp->next = cur->next;
				temp->start = sack.end;
				temp->end = cur->end;
				temp->dups = cur->dups;
				temp->rxmit = SEQ_MAX(cur->rxmit, temp->start);
				cur->end = sack.start;
				cur->rxmit = SEQ_MIN(cur->rxmit, cur->end);
				cur->dups++;
				if (((sack.end - cur->end)/tp->t_maxseg) >=
					tcprexmtthresh)
					cur->dups = tcprexmtthresh;
				cur->next = temp;
				p = temp;
				cur = p->next;
				tp->snd_numholes++;
			}
		}
		/* At this point, p points to the last hole on the list */
		if (SEQ_LT(tp->rcv_lastsack, sack.start)) {
			/*
			 * Need to append new hole at end.
			 * Last hole is p (and it's not NULL).
			 */
			if (tp->snd_numholes >= TCP_SACKHOLE_LIMIT)
				goto done;
			temp = (struct sackhole *)
			    pool_get(&sackhl_pool, PR_NOWAIT);
			if (temp == NULL)
				goto done; /* ENOBUFS */
			temp->start = tp->rcv_lastsack;
			temp->end = sack.start;
			temp->dups = min(tcprexmtthresh,
			    ((sack.end - sack.start)/tp->t_maxseg));
			if (temp->dups < 1)
				temp->dups = 1;
			temp->rxmit = temp->start;
			temp->next = 0;
			p->next = temp;
			tp->rcv_lastsack = sack.end;
			tp->snd_numholes++;
		}
	}
done:
	return;
}


Fix integer overflow in chunk_split

Reviewed By: @ptarjan

Differential Revision: D1515947

String string_chunk_split(const char *src, int srclen, const char *end,
                          int endlen, int chunklen) {
  int chunks = srclen / chunklen; // complete chunks!
  int restlen = srclen - chunks * chunklen; /* srclen % chunklen */

  int out_len = (chunks + 1) * endlen + srclen;
  String ret(out_len, ReserveString);
  char *dest = ret.bufferSlice().ptr;

  const char *p; char *q;
  const char *pMax = src + srclen - chunklen + 1;
  for (p = src, q = dest; p < pMax; ) {
    memcpy(q, p, chunklen);
    q += chunklen;
    memcpy(q, end, endlen);
    q += endlen;
    p += chunklen;
  }

  if (restlen) {
    memcpy(q, p, restlen);
    q += restlen;
    memcpy(q, end, endlen);
    q += endlen;
  }

  ret.setSize(q - dest);
  return ret;
}

Integer overflow in the string_chunk_split function in hphp/runtime/base/zend-string.cpp in Facebook HipHop Virtual Machine (HHVM) before 3.3.0 allows remote attackers to cause a denial of service (application crash) or possibly have unspecified other impact via crafted arguments to the chunk_split function.


String string_chunk_split(const char *src, int srclen, const char *end,
                          int endlen, int chunklen) {
  int chunks = srclen / chunklen; // complete chunks!
  int restlen = srclen - chunks * chunklen; /* srclen % chunklen */

  String ret(
    safe_address(
      chunks + 1,
      endlen,
      srclen
    ),
    ReserveString
  );
  char *dest = ret.bufferSlice().ptr;

  const char *p; char *q;
  const char *pMax = src + srclen - chunklen + 1;
  for (p = src, q = dest; p < pMax; ) {
    memcpy(q, p, chunklen);
    q += chunklen;
    memcpy(q, end, endlen);
    q += endlen;
    p += chunklen;
  }

  if (restlen) {
    memcpy(q, p, restlen);
    q += restlen;
    memcpy(q, end, endlen);
    q += endlen;
  }

  ret.setSize(q - dest);
  return ret;
}