Commit message: Port for for bug #68552
CVE: CVE-2014-9705
CWE: CWE-119
Vulnerability description: Heap-based buffer overflow in the enchant_broker_request_dict function in ext/enchant/enchant.c in PHP before 5.4.38, 5.5.x before 5.5.22, and 5.6.x before 5.6.6 allows remote attackers to execute arbitrary code via vectors that trigger creation of multiple dictionaries.

Project: php-src
File: enchant.c

PHP_FUNCTION(enchant_broker_set_dict_path)
{
	zval *broker;
	enchant_broker *pbroker;
	long dict_type;
	char *value;
	int value_len;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rls", &broker, &dict_type, &value, &value_len) == FAILURE) {
		RETURN_FALSE;
	}

	if (!value_len) {
		RETURN_FALSE;
	}
	
	PHP_ENCHANT_GET_BROKER;

	switch (dict_type) {
		case PHP_ENCHANT_MYSPELL:
			PHP_ENCHANT_GET_BROKER;
			enchant_broker_set_param(pbroker->pbroker, "enchant.myspell.dictionary.path", (const char *)value);
			RETURN_TRUE;
			break;

		case PHP_ENCHANT_ISPELL:
			PHP_ENCHANT_GET_BROKER;
			enchant_broker_set_param(pbroker->pbroker, "enchant.ispell.dictionary.path", (const char *)value);
			RETURN_TRUE;
			break;

		default:
			RETURN_FALSE;
	}
}

Project: php-src
File: enchant.c

static void __enumerate_providers_fn (const char * const name,
                        const char * const desc,
                        const char * const file,
                        void * ud) /* {{{ */
{
	php_info_print_table_row(3, name, desc, file);
}

Project: php-src
File: enchant.c

PHP_FUNCTION(enchant_broker_set_dict_path)
{
	RETURN_FALSE;
}

Project: php-src
File: enchant.c

PHP_FUNCTION(enchant_broker_init)
{
	enchant_broker *broker;
	EnchantBroker *pbroker;

	if (zend_parse_parameters_none() == FAILURE) {
		return;
	}

	pbroker = enchant_broker_init();

	if (pbroker) {
		broker = (enchant_broker *) emalloc(sizeof(enchant_broker));
		broker->pbroker = pbroker;
		broker->dict = NULL;
		broker->dictcnt = 0;
		broker->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, broker, le_enchant_broker);
	} else {
		RETURN_FALSE;
	}
}

Project: php-src
File: enchant.c

PHP_MINIT_FUNCTION(enchant)
{
	le_enchant_broker = zend_register_list_destructors_ex(php_enchant_broker_free, NULL, "enchant_broker", module_number);
	le_enchant_dict = zend_register_list_destructors_ex(php_enchant_dict_free, NULL, "enchant_dict", module_number);
	REGISTER_LONG_CONSTANT("ENCHANT_MYSPELL", PHP_ENCHANT_MYSPELL, CONST_CS | CONST_PERSISTENT);
	REGISTER_LONG_CONSTANT("ENCHANT_ISPELL", PHP_ENCHANT_ISPELL, CONST_CS | CONST_PERSISTENT);
	return SUCCESS;
}

Project: php-src
File: enchant.c

PHP_FUNCTION(enchant_broker_get_dict_path)
{
	RETURN_FALSE;
}

Project: php-src
File: enchant.c

PHP_FUNCTION(enchant_broker_request_dict)
{
	zval *broker;
	enchant_broker *pbroker;
	enchant_dict *dict;
	EnchantDict *d;
	char *tag;
	int taglen;
	int pos;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &broker, &tag, &taglen) == FAILURE) {
		RETURN_FALSE;
	}

	PHP_ENCHANT_GET_BROKER;
	
	if (taglen == 0) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Tag cannot be empty");
		RETURN_FALSE;
	}

	d = enchant_broker_request_dict(pbroker->pbroker, (const char *)tag);
	if (d) {
		pos = pbroker->dictcnt++;
		if (pbroker->dictcnt) {
			pbroker->dict = (enchant_dict **)erealloc(pbroker->dict, sizeof(enchant_dict *) * pbroker->dictcnt);
		} else {
			pbroker->dict = (enchant_dict **)emalloc(sizeof(enchant_dict *));
			pos = 0;
		}

		dict = pbroker->dict[pos] = (enchant_dict *)emalloc(sizeof(enchant_dict));
		dict->id = pos;
		dict->pbroker = pbroker;
		dict->pdict = d;
		dict->prev = pos ? pbroker->dict[pos-1] : NULL;
		dict->next = NULL;
		pbroker->dict[pos] = dict;

		if (pos) {
			pbroker->dict[pos-1]->next = dict;
		}

		dict->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, dict, le_enchant_dict);
		zend_list_addref(pbroker->rsrc_id);
	} else {
		RETURN_FALSE;
	}
}

Project: php-src
File: enchant.c

PHP_FUNCTION(enchant_broker_get_dict_path)
{
	zval *broker;
	enchant_broker *pbroker;
	long dict_type;
	char *value;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &broker, &dict_type) == FAILURE) {
		RETURN_FALSE;
	}
	
	PHP_ENCHANT_GET_BROKER;

	switch (dict_type) {
		case PHP_ENCHANT_MYSPELL:
			PHP_ENCHANT_GET_BROKER;
			value = enchant_broker_get_param(pbroker->pbroker, "enchant.myspell.dictionary.path");
			break;

		case PHP_ENCHANT_ISPELL:
			PHP_ENCHANT_GET_BROKER;
			value = enchant_broker_get_param(pbroker->pbroker, "enchant.ispell.dictionary.path");
			break;

		default:
			RETURN_FALSE;
	}

	RETURN_STRING(value, 1);
}

Project: php-src
File: enchant.c

static void php_enchant_broker_free(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */
{
	if (rsrc->ptr) {
		enchant_broker *broker = (enchant_broker *)rsrc->ptr;
		if (broker) {
			if (broker->pbroker) {
				if (broker->dictcnt && broker->dict) {
					if (broker->dict) {
						int total;
						total = broker->dictcnt-1;
						do {
							zend_list_delete(broker->dict[total]->rsrc_id);
							efree(broker->dict[total]);
							total--;
						} while (total>=0);
					}
					efree(broker->dict);
					broker->dict = NULL;
				}
				enchant_broker_free(broker->pbroker);
			}
			efree(broker);
		}
	}
}

Project: php-src
File: enchant.c

PHP_FUNCTION(enchant_broker_list_dicts)
{
	zval *broker;
	enchant_broker *pbroker;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &broker) == FAILURE) {
		RETURN_FALSE;
	}

	PHP_ENCHANT_GET_BROKER;

	enchant_broker_list_dicts(pbroker->pbroker, php_enchant_list_dicts_fn, (void *)return_value);
}

Project: php-src
File: enchant.c

PHP_MSHUTDOWN_FUNCTION(enchant)
{
	return SUCCESS;
}

Project: php-src
File: enchant.c

static void php_enchant_list_dicts_fn( const char * const lang_tag,
	   	const char * const provider_name, const char * const provider_desc,
		const char * const provider_file, void * ud) /* {{{ */
{
	zval *zdesc = (zval *) ud;
	zval *tmp_array;

	MAKE_STD_ZVAL(tmp_array);
	array_init(tmp_array);
	add_assoc_string(tmp_array, "lang_tag", (char *)lang_tag, 1);
	add_assoc_string(tmp_array, "provider_name", (char *)provider_name, 1);
	add_assoc_string(tmp_array, "provider_desc", (char *)provider_desc, 1);
	add_assoc_string(tmp_array, "provider_file", (char *)provider_file, 1);

	if (Z_TYPE_P(zdesc) != IS_ARRAY) {
		array_init(zdesc);
	}
	add_next_index_zval(zdesc, tmp_array);

}

Project: php-src
File: enchant.c

enumerate_providers_fn (const char * const name,
                        const char * const desc,
                        const char * const file,
                        void * ud) /* {{{ */
{
	zval *zdesc = (zval *) ud;
	zval *tmp_array;

	MAKE_STD_ZVAL(tmp_array);
	array_init(tmp_array);

	add_assoc_string(tmp_array, "name", (char *)name, 1);
	add_assoc_string(tmp_array, "desc", (char *)desc, 1);
	add_assoc_string(tmp_array, "file", (char *)file, 1);

	if (Z_TYPE_P(zdesc)!=IS_ARRAY) {
		array_init(zdesc);
	}

	add_next_index_zval(zdesc, tmp_array);
}

Project: php-src
File: enchant.c

PHP_MINFO_FUNCTION(enchant)
{
	EnchantBroker *pbroker;

	pbroker = enchant_broker_init();
	php_info_print_table_start();
	php_info_print_table_header(2, "enchant support", "enabled");
	php_info_print_table_row(2, "Version", PHP_ENCHANT_VERSION);
#ifdef ENCHANT_VERSION_STRING
	php_info_print_table_row(2, "Libenchant Version", ENCHANT_VERSION_STRING);
#elif defined(HAVE_ENCHANT_BROKER_SET_PARAM)
	php_info_print_table_row(2, "Libenchant Version", "1.5.0 or later");
#endif
	php_info_print_table_row(2, "Revision", "$Id$");
	php_info_print_table_end();

	php_info_print_table_start();
	enchant_broker_describe(pbroker, __enumerate_providers_fn, NULL);
	php_info_print_table_end();
	enchant_broker_free(pbroker);
}

Project: php-src
File: enchant.c

PHP_FUNCTION(enchant_broker_get_error)
{
	zval *broker;
	enchant_broker *pbroker;
	char *msg;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &broker) == FAILURE) {
		RETURN_FALSE;
	}

	PHP_ENCHANT_GET_BROKER;

	msg = enchant_broker_get_error(pbroker->pbroker);
	if (msg) {
		RETURN_STRING((char *)msg, 1);
	}
	RETURN_FALSE;
}

Project: php-src
File: enchant.c

static void php_enchant_dict_free(zend_rsrc_list_entry *rsrc TSRMLS_DC) /* {{{ */

{
	if (rsrc->ptr) {
		enchant_dict *pdict = (enchant_dict *)rsrc->ptr;
		if (pdict) {
			if (pdict->pdict && pdict->pbroker) {
				enchant_broker_free_dict(pdict->pbroker->pbroker, pdict->pdict);
				if (pdict->id) {
					pdict->pbroker->dict[pdict->id-1]->next = NULL;
				}
				zend_list_delete(pdict->pbroker->rsrc_id);
			}

		}
	}
}

Project: php-src
File: enchant.c

describe_dict_fn (const char * const lang,
                  const char * const name,
                  const char * const desc,
                  const char * const file,
                  void * ud) /* {{{ */
{
	zval *zdesc = (zval *) ud;
	array_init(zdesc);
	add_assoc_string(zdesc, "lang", (char *)lang, 1);
	add_assoc_string(zdesc, "name", (char *)name, 1);
	add_assoc_string(zdesc, "desc", (char *)desc, 1);
	add_assoc_string(zdesc, "file", (char *)file, 1);
}

Project: php-src
File: enchant.c

PHP_FUNCTION(enchant_broker_free)
{
	zval *broker;
	enchant_broker *pbroker;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &broker) == FAILURE) {
		RETURN_FALSE;
	}
	PHP_ENCHANT_GET_BROKER;

	zend_list_delete(Z_RESVAL_P(broker));
	RETURN_TRUE;
}