Commit message: Fix bug #71459 - Integer overflow in iptcembed()
CVE: None
Vulnerability description: None

Project: php-src
File: iptc.c

PHP_FUNCTION(iptcparse)
{
	int inx = 0, len;
	unsigned int tagsfound = 0;
	unsigned char *buffer, recnum, dataset, key[ 16 ];
	char *str;
	int str_len;
	zval *values, **element;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) != SUCCESS) {
		return;
	}

	buffer = (unsigned char *)str;

	while (inx < str_len) { /* find 1st tag */
		if ((buffer[inx] == 0x1c) && ((buffer[inx+1] == 0x01) || (buffer[inx+1] == 0x02))){ 
			break;
		} else {
			inx++;
		}
	}

	while (inx < str_len) {
		if (buffer[ inx++ ] != 0x1c) {
			break;   /* we ran against some data which does not conform to IPTC - stop parsing! */
		} 
		
		if ((inx + 4) >= str_len)
			break;

		dataset = buffer[ inx++ ];
		recnum = buffer[ inx++ ];

		if (buffer[ inx ] & (unsigned char) 0x80) { /* long tag */
			if((inx+6) >= str_len) {
				break;
			}
			len = (((long) buffer[ inx + 2 ]) << 24) + (((long) buffer[ inx + 3 ]) << 16) + 
				  (((long) buffer[ inx + 4 ]) <<  8) + (((long) buffer[ inx + 5 ]));
			inx += 6;
		} else { /* short tag */
			len = (((unsigned short) buffer[ inx ])<<8) | (unsigned short)buffer[ inx+1 ];
			inx += 2;
		}
		
		if ((len < 0) || (len > str_len) || (inx + len) > str_len) {
			break;
		}

		snprintf(key, sizeof(key), "%d#%03d", (unsigned int) dataset, (unsigned int) recnum);

		if (tagsfound == 0) { /* found the 1st tag - initialize the return array */
			array_init(return_value);
		}

		if (zend_hash_find(Z_ARRVAL_P(return_value), key, strlen(key) + 1, (void **) &element) == FAILURE) {
			MAKE_STD_ZVAL(values);
			array_init(values);
			
			zend_hash_update(Z_ARRVAL_P(return_value), key, strlen(key) + 1, (void *) &values, sizeof(zval*), (void **) &element);
		} 
			
		add_next_index_stringl(*element, buffer+inx, len, 1);
		inx += len;
		tagsfound++;
	}

	if (! tagsfound) {
		RETURN_FALSE;
	}
}

Project: php-src
File: iptc.c

PHP_FUNCTION(iptcembed)
{
	char *iptcdata, *jpeg_file;
	int iptcdata_len, jpeg_file_len;
	long spool = 0;
	FILE *fp;
	unsigned int marker, done = 0;
	int inx;
	unsigned char *spoolbuf = NULL, *poi = NULL;
	struct stat sb;
	zend_bool written = 0;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sp|l", &iptcdata, &iptcdata_len, &jpeg_file, &jpeg_file_len, &spool) != SUCCESS) {
		return;
	}

	if (php_check_open_basedir(jpeg_file TSRMLS_CC)) {
		RETURN_FALSE;
	}

	if ((size_t)iptcdata_len >= SIZE_MAX - sizeof(psheader) - 1025) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "IPTC data too large");
		RETURN_FALSE;
	}

	if ((fp = VCWD_FOPEN(jpeg_file, "rb")) == 0) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to open %s", jpeg_file);
		RETURN_FALSE;
	}

	if (spool < 2) {
		fstat(fileno(fp), &sb);

		poi = spoolbuf = safe_emalloc(1, (size_t)iptcdata_len + sizeof(psheader) + 1024 + 1, sb.st_size);
		memset(poi, 0, iptcdata_len + sizeof(psheader) + sb.st_size + 1024 + 1);
	} 

	if (php_iptc_get1(fp, spool, poi?&poi:0 TSRMLS_CC) != 0xFF) {
		fclose(fp);
		if (spoolbuf) {
			efree(spoolbuf);
		}
		RETURN_FALSE;
	}

	if (php_iptc_get1(fp, spool, poi?&poi:0 TSRMLS_CC) != 0xD8) {
		fclose(fp);
		if (spoolbuf) {
			efree(spoolbuf);
		}
		RETURN_FALSE;
	}

	while (!done) {
		marker = php_iptc_next_marker(fp, spool, poi?&poi:0 TSRMLS_CC);

		if (marker == M_EOI) { /* EOF */
			break;
		} else if (marker != M_APP13) { 
			php_iptc_put1(fp, spool, (unsigned char)marker, poi?&poi:0 TSRMLS_CC);
		}

		switch (marker) {
			case M_APP13:
				/* we are going to write a new APP13 marker, so don't output the old one */
				php_iptc_skip_variable(fp, 0, 0 TSRMLS_CC);    
				php_iptc_read_remaining(fp, spool, poi?&poi:0 TSRMLS_CC);
				done = 1;
				break;

			case M_APP0:
				/* APP0 is in each and every JPEG, so when we hit APP0 we insert our new APP13! */
			case M_APP1:
				if (written) {
					/* don't try to write the data twice */
					break;
				}
				written = 1;

				php_iptc_skip_variable(fp, spool, poi?&poi:0 TSRMLS_CC);

				if (iptcdata_len & 1) {
					iptcdata_len++; /* make the length even */
				}

				psheader[ 2 ] = (iptcdata_len+28)>>8;
				psheader[ 3 ] = (iptcdata_len+28)&0xff;

				for (inx = 0; inx < 28; inx++) {
					php_iptc_put1(fp, spool, psheader[inx], poi?&poi:0 TSRMLS_CC);
				}

				php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len>>8), poi?&poi:0 TSRMLS_CC);
				php_iptc_put1(fp, spool, (unsigned char)(iptcdata_len&0xff), poi?&poi:0 TSRMLS_CC);

				for (inx = 0; inx < iptcdata_len; inx++) {
					php_iptc_put1(fp, spool, iptcdata[inx], poi?&poi:0 TSRMLS_CC);
				}
				break;

			case M_SOS:								
				/* we hit data, no more marker-inserting can be done! */
				php_iptc_read_remaining(fp, spool, poi?&poi:0 TSRMLS_CC);
				done = 1;
				break;

			default:
				php_iptc_skip_variable(fp, spool, poi?&poi:0 TSRMLS_CC);
				break;
		}
	}

	fclose(fp);

	if (spool < 2) {
		RETVAL_STRINGL(spoolbuf, poi - spoolbuf, 0);
	} else {
		RETURN_TRUE;
	}
}