From 85f5451a3e9fad7cb9728ba5408b433fca22b44b Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 24 Apr 2007 10:12:31 +0000 Subject: [PATCH] * Fixed crashes with repeated registering/unregisitering of request methods (noticed by Andreas Weber) --- http.c | 43 +++--- http_request_method_api.c | 279 ++++++++++++++++++---------------- package2.xml | 10 +- php_http.h | 9 +- php_http_request_method_api.h | 10 +- php_http_std_defs.h | 3 + 6 files changed, 180 insertions(+), 174 deletions(-) diff --git a/http.c b/http.c index 4f4eca3..0299d2f 100644 --- a/http.c +++ b/http.c @@ -200,6 +200,14 @@ static inline void _http_globals_free(zend_http_globals *G TSRMLS_DC) G->server_var = NULL; } } + +#if PHP_DEBUG +zend_http_globals *http_globals(void) +{ + TSRMLS_FETCH(); + return HTTP_G; +} +#endif /* }}} */ /* {{{ static inline void http_check_allowed_methods(char *) */ @@ -242,7 +250,7 @@ PHP_INI_BEGIN() HTTP_PHP_INI_ENTRY("http.log.allowed_methods", "", PHP_INI_ALL, OnUpdateString, log.allowed_methods) HTTP_PHP_INI_ENTRY("http.log.composite", "", PHP_INI_ALL, OnUpdateString, log.composite) HTTP_PHP_INI_ENTRY("http.request.methods.allowed", "", PHP_INI_ALL, http_update_allowed_methods, request.methods.allowed) - HTTP_PHP_INI_ENTRY("http.request.methods.custom", "", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateString, request.methods.custom.ini) + HTTP_PHP_INI_ENTRY("http.request.methods.custom", "", PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateString, request.methods.custom) #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) HTTP_PHP_INI_ENTRY("http.request.datashare.cookie", "0", PHP_INI_SYSTEM, OnUpdateBool, request.datashare.cookie) HTTP_PHP_INI_ENTRY("http.request.datashare.dns", "1", PHP_INI_SYSTEM, OnUpdateBool, request.datashare.dns) @@ -489,31 +497,22 @@ PHP_MINFO_FUNCTION(http) php_info_print_table_start(); php_info_print_table_colspan_header(2, "Request Methods"); { - int i; - phpstr *custom_request_methods = phpstr_new(); - phpstr *known_request_methods = phpstr_from_string(HTTP_KNOWN_METHODS, lenof(HTTP_KNOWN_METHODS)); - http_request_method_entry **ptr = HTTP_G->request.methods.custom.entries; - - for (i = 0; i < HTTP_G->request.methods.custom.count; ++i) { - if (ptr[i]) { - phpstr_appendf(custom_request_methods, "%s, ", ptr[i]->name); + HashPosition pos; + phpstr *methods = phpstr_new(); + char **name; + + FOREACH_HASH_VAL(pos, &HTTP_G->request.methods.registered, name) { + if (pos->h) { + phpstr_appendf(methods, "%s, ", *name); } } - - phpstr_append(known_request_methods, PHPSTR_VAL(custom_request_methods), PHPSTR_LEN(custom_request_methods)); - phpstr_fix(known_request_methods); - phpstr_fix(custom_request_methods); - - php_info_print_table_row(2, "Known", PHPSTR_VAL(known_request_methods)); - php_info_print_table_row(2, "Custom", - PHPSTR_LEN(custom_request_methods) ? PHPSTR_VAL(custom_request_methods) : "none registered"); - php_info_print_table_row(2, "Allowed", strlen(HTTP_G->request.methods.allowed) ? HTTP_G->request.methods.allowed : "(ANY)"); - - phpstr_free(&known_request_methods); - phpstr_free(&custom_request_methods); + phpstr_fix(methods); + php_info_print_table_row(2, "Registered", PHPSTR_VAL(methods)); + php_info_print_table_row(2, "Allowed", *HTTP_G->request.methods.allowed ? HTTP_G->request.methods.allowed : "(ANY)"); + phpstr_free(&methods); } php_info_print_table_end(); - + DISPLAY_INI_ENTRIES(); } /* }}} */ diff --git a/http_request_method_api.c b/http_request_method_api.c index b587474..12b0170 100644 --- a/http_request_method_api.c +++ b/http_request_method_api.c @@ -99,197 +99,216 @@ PHP_MINIT_FUNCTION(http_request_method) return SUCCESS; } -PHP_RINIT_FUNCTION(http_request_method) +static void free_method(void *el) { - HTTP_G->request.methods.custom.entries = ecalloc(1, sizeof(http_request_method_entry *)); - - if (HTTP_G->request.methods.custom.ini && *HTTP_G->request.methods.custom.ini) { - HashPosition pos; - HashTable methods; - zval **data; - - zend_hash_init(&methods, 0, NULL, ZVAL_PTR_DTOR, 0); - http_parse_params(HTTP_G->request.methods.custom.ini, HTTP_PARAMS_DEFAULT, &methods); - FOREACH_HASH_VAL(pos, &methods, data) { - if (Z_TYPE_PP(data) == IS_STRING) { - http_request_method_register(Z_STRVAL_PP(data), Z_STRLEN_PP(data)); - } - } - zend_hash_destroy(&methods); - } - return SUCCESS; + efree(*(char **)el); } -PHP_RSHUTDOWN_FUNCTION(http_request_method) +static void unregister_method(const char *name TSRMLS_DC) { - int i; - http_request_method_entry **ptr = HTTP_G->request.methods.custom.entries; + char *ptr, tmp[sizeof("HTTP_METH_") + HTTP_REQUEST_METHOD_MAXLEN] = "HTTP_METH_"; - for (i = 0; i < HTTP_G->request.methods.custom.count; ++i) { - if (ptr[i]) { - http_request_method_unregister(HTTP_CUSTOM_REQUEST_METHOD_START + i); + strlcpy(tmp + lenof("HTTP_METH_"), name, HTTP_REQUEST_METHOD_MAXLEN); + for (ptr = tmp + lenof("HTTP_METH_"); *ptr; ++ptr) { + if (*ptr == '-') { + *ptr = '_'; } } - efree(HTTP_G->request.methods.custom.entries); - return SUCCESS; -} -/* }}} */ - -/* {{{ char *http_request_method_name(http_request_method) */ -PHP_HTTP_API const char *_http_request_method_name(http_request_method m TSRMLS_DC) -{ - http_request_method_entry **ptr = HTTP_G->request.methods.custom.entries; - - if (HTTP_STD_REQUEST_METHOD(m)) { - return http_request_methods[m]; +#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) && !defined(WONKY) + if (SUCCESS != zend_hash_del(&http_request_object_ce->constants_table, tmp + lenof("HTTP_"), strlen(tmp + lenof("HTTP_")) + 1)) { + http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Could not unregister request method: HttpRequest::%s", tmp + lenof("HTTP_")); } - - if ( (HTTP_CUSTOM_REQUEST_METHOD(m) >= 0) && - (HTTP_CUSTOM_REQUEST_METHOD(m) < HTTP_G->request.methods.custom.count) && - (ptr[HTTP_CUSTOM_REQUEST_METHOD(m)])) { - return ptr[HTTP_CUSTOM_REQUEST_METHOD(m)]->name; +#endif + if (SUCCESS != zend_hash_del(EG(zend_constants), tmp, strlen(tmp) + 1)) { + http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Could not unregister request method: %s", tmp); } - - return http_request_methods[0]; } -/* }}} */ -/* {{{ int http_request_method_exists(zend_bool, ulong, char *) */ -PHP_HTTP_API int _http_request_method_exists(zend_bool by_name, http_request_method id, const char *name TSRMLS_DC) +PHP_RINIT_FUNCTION(http_request_method) { - int i; - http_request_method_entry **ptr = HTTP_G->request.methods.custom.entries; + HashTable ht; - if (by_name) { - for (i = HTTP_MIN_REQUEST_METHOD; i < HTTP_MAX_REQUEST_METHOD; ++i) { - if (!strcasecmp(name, http_request_methods[i])) { - return i; - } - } - for (i = 0; i < HTTP_G->request.methods.custom.count; ++i) { - if (ptr[i] && !strcasecmp(name, ptr[i]->name)) { - return HTTP_CUSTOM_REQUEST_METHOD_START + i; + zend_hash_init(&HTTP_G->request.methods.registered, 0, NULL, free_method, 0); +#define HTTP_METH_REG(m) \ + { \ + char *_m=estrdup(m); \ + zend_hash_next_index_insert(&HTTP_G->request.methods.registered, (void *) &_m, sizeof(char *), NULL); \ + } + HTTP_METH_REG("UNKNOWN"); + /* HTTP/1.1 */ + HTTP_METH_REG("GET"); + HTTP_METH_REG("HEAD"); + HTTP_METH_REG("POST"); + HTTP_METH_REG("PUT"); + HTTP_METH_REG("DELETE"); + HTTP_METH_REG("OPTIONS"); + HTTP_METH_REG("TRACE"); + HTTP_METH_REG("CONNECT"); + /* WebDAV - RFC 2518 */ + HTTP_METH_REG("PROPFIND"); + HTTP_METH_REG("PROPPATCH"); + HTTP_METH_REG("MKCOL"); + HTTP_METH_REG("COPY"); + HTTP_METH_REG("MOVE"); + HTTP_METH_REG("LOCK"); + HTTP_METH_REG("UNLOCK"); + /* WebDAV Versioning - RFC 3253 */ + HTTP_METH_REG("VERSION-CONTROL"); + HTTP_METH_REG("REPORT"); + HTTP_METH_REG("CHECKOUT"); + HTTP_METH_REG("CHECKIN"); + HTTP_METH_REG("UNCHECKOUT"); + HTTP_METH_REG("MKWORKSPACE"); + HTTP_METH_REG("UPDATE"); + HTTP_METH_REG("LABEL"); + HTTP_METH_REG("MERGE"); + HTTP_METH_REG("BASELINE-CONTROL"); + HTTP_METH_REG("MKACTIVITY"); + /* WebDAV Access Control - RFC 3744 */ + HTTP_METH_REG("ACL"); + + zend_hash_init(&ht, 0, NULL, ZVAL_PTR_DTOR, 0); + if (*HTTP_G->request.methods.custom && SUCCESS == http_parse_params(HTTP_G->request.methods.custom, HTTP_PARAMS_DEFAULT, &ht)) { + HashPosition pos; + zval **val; + + FOREACH_HASH_VAL(pos, &ht, val) { + if (Z_TYPE_PP(val) == IS_STRING) { + http_request_method_register(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); } } - } else if (HTTP_STD_REQUEST_METHOD(id)) { - return id; - } else if ( (HTTP_CUSTOM_REQUEST_METHOD(id) >= 0) && - (HTTP_CUSTOM_REQUEST_METHOD(id) < HTTP_G->request.methods.custom.count) && - (ptr[HTTP_CUSTOM_REQUEST_METHOD(id)])) { - return id; } + zend_hash_destroy(&ht); - return 0; + return SUCCESS; } -/* }}} */ -/* {{{ int http_request_method_register(char *) */ -PHP_HTTP_API int _http_request_method_register(const char *method_name, int method_name_len TSRMLS_DC) +PHP_RSHUTDOWN_FUNCTION(http_request_method) { - int i, meth_num; - char *http_method, *method, *mconst; - http_request_method_entry **ptr = HTTP_G->request.methods.custom.entries; + char **name; + int i, c = zend_hash_next_free_element(&HTTP_G->request.methods.registered); - if (!HTTP_IS_CTYPE(alpha, *method_name)) { - http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Request method does not start with a character (%s)", method_name); - return 0; + for (i = HTTP_MAX_REQUEST_METHOD; i < c; ++i) { + if (SUCCESS == zend_hash_index_find(&HTTP_G->request.methods.registered, i, (void *) &name)) { + unregister_method(*name TSRMLS_CC); + } } - if (http_request_method_exists(1, 0, method_name)) { - http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Request method does already exist (%s)", method_name); - return 0; + zend_hash_destroy(&HTTP_G->request.methods.registered); + return SUCCESS; +} + +#define http_request_method_cncl(m, c) _http_request_method_cncl_ex((m), strlen(m), (c) TSRMLS_CC) +#define http_request_method_cncl_ex(m, l, c) _http_request_method_cncl_ex((m), (l), (c) TSRMLS_CC) +static STATUS _http_request_method_cncl_ex(const char *method_name, int method_name_len, char **cnst TSRMLS_DC) +{ + int i; + char *cncl; + + if (method_name_len >= HTTP_REQUEST_METHOD_MAXLEN) { + http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Request method too long (%s)", method_name); } + cncl = emalloc(method_name_len + 1); - method = emalloc(method_name_len + 1); - mconst = emalloc(method_name_len + 1); for (i = 0; i < method_name_len; ++i) { switch (method_name[i]) { case '-': - method[i] = '-'; - mconst[i] = '_'; + cncl[i] = '-'; break; default: if (!HTTP_IS_CTYPE(alnum, method_name[i])) { - efree(method); - efree(mconst); + efree(cncl); http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Request method contains illegal characters (%s)", method_name); - return 0; + return FAILURE; } - mconst[i] = method[i] = HTTP_TO_CTYPE(upper, method_name[i]); + cncl[i] = HTTP_TO_CTYPE(upper, method_name[i]); break; } } - method[method_name_len] = '\0'; - mconst[method_name_len] = '\0'; + cncl[method_name_len] = '\0'; - ptr = erealloc(ptr, sizeof(http_request_method_entry *) * (HTTP_G->request.methods.custom.count + 1)); - HTTP_G->request.methods.custom.entries = ptr; - ptr[HTTP_G->request.methods.custom.count] = emalloc(sizeof(http_request_method_entry)); - ptr[HTTP_G->request.methods.custom.count]->name = method; - ptr[HTTP_G->request.methods.custom.count]->cnst = mconst; - meth_num = HTTP_CUSTOM_REQUEST_METHOD_START + HTTP_G->request.methods.custom.count++; + *cnst = cncl; + return SUCCESS; +} - method_name_len = spprintf(&http_method, 0, "HTTP_METH_%s", mconst); - zend_register_long_constant(http_method, method_name_len + 1, meth_num, CONST_CS, http_module_number TSRMLS_CC); - efree(http_method); +PHP_HTTP_API const char *_http_request_method_name(http_request_method m TSRMLS_DC) +{ + char **name; + if (SUCCESS == zend_hash_index_find(&HTTP_G->request.methods.registered, m, (void *) &name)) { + return *name; + } + return "UNKNOWN"; +} + +PHP_HTTP_API int _http_request_method_exists(int by_name, http_request_method id_num, const char *id_str TSRMLS_DC) +{ + char *id_dup; + + if (by_name && (SUCCESS == http_request_method_cncl(id_str, &id_dup))) { + char **name; + HashPosition pos; + HashKey key = initHashKey(0); + + FOREACH_HASH_KEYVAL(pos, &HTTP_G->request.methods.registered, key, name) { + if (key.type == HASH_KEY_IS_LONG && !strcmp(*name, id_dup)) { + efree(id_dup); + return key.num; + } + } + efree(id_dup); + } else if (zend_hash_index_exists(&HTTP_G->request.methods.registered, id_num)){ + return id_num; + } + return 0; +} + +PHP_HTTP_API int _http_request_method_register(const char *method_str, int method_len TSRMLS_DC) +{ + char *method_dup, *ptr, tmp[sizeof("HTTP_METH_") + HTTP_REQUEST_METHOD_MAXLEN] = "HTTP_METH_"; + int method_num = http_request_method_exists(1, 0, method_str); + + if (!method_num && (SUCCESS == http_request_method_cncl_ex(method_str, method_len, &method_dup))) { + method_num = zend_hash_next_free_element(&HTTP_G->request.methods.registered); + zend_hash_index_update(&HTTP_G->request.methods.registered, method_num, (void *) &method_dup, sizeof(char *), NULL); + + strlcpy(tmp + lenof("HTTP_METH_"), method_dup, HTTP_REQUEST_METHOD_MAXLEN); + for (ptr = tmp + lenof("HTTP_METH_"); *ptr; ++ptr) { + if (*ptr == '-') { + *ptr = '_'; + } + } + + zend_register_long_constant(tmp, strlen(tmp) + 1, method_num, CONST_CS, http_module_number TSRMLS_CC); #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) && !defined(WONKY) - method_name_len = spprintf(&http_method, 0, "METH_%s", mconst); - zend_declare_class_constant_long(http_request_object_ce, http_method, method_name_len, meth_num TSRMLS_CC); - efree(http_method); + zend_declare_class_constant_long(http_request_object_ce, tmp + lenof("HTTP_"), strlen(tmp + lenof("HTTP_")), method_num TSRMLS_CC); #endif + } - return meth_num; + return method_num; } -/* }}} */ -/* {{{ STATUS http_request_method_unregister(int) */ PHP_HTTP_API STATUS _http_request_method_unregister(int method TSRMLS_DC) { - char *http_method; - int method_len; - http_request_method_entry **ptr = HTTP_G->request.methods.custom.entries; + char **name; if (HTTP_STD_REQUEST_METHOD(method)) { http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Standard request methods cannot be unregistered"); return FAILURE; } - if ( (HTTP_CUSTOM_REQUEST_METHOD(method) < 0) || - (HTTP_CUSTOM_REQUEST_METHOD(method) > HTTP_G->request.methods.custom.count) || - (!ptr[HTTP_CUSTOM_REQUEST_METHOD(method)])) { + if (SUCCESS != zend_hash_index_find(&HTTP_G->request.methods.registered, method, (void *) &name)) { http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Custom request method with id %d does not exist", method); return FAILURE; } -#if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) && !defined(WONKY) - method_len = spprintf(&http_method, 0, "METH_%s", ptr[HTTP_CUSTOM_REQUEST_METHOD(method)]->cnst); - if (SUCCESS != zend_hash_del(&http_request_object_ce->constants_table, http_method, method_len + 1)) { - http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Could not unregister request method: HttpRequest::%s", http_method); - efree(http_method); - return FAILURE; - } - efree(http_method); -#endif - - method_len = spprintf(&http_method, 0, "HTTP_METH_%s", ptr[HTTP_CUSTOM_REQUEST_METHOD(method)]->cnst); - if (SUCCESS != zend_hash_del(EG(zend_constants), http_method, method_len + 1)) { - http_error_ex(HE_NOTICE, HTTP_E_REQUEST_METHOD, "Could not unregister request method: %s", http_method); - efree(http_method); - return FAILURE; - } - efree(http_method); - - efree(ptr[HTTP_CUSTOM_REQUEST_METHOD(method)]->name); - efree(ptr[HTTP_CUSTOM_REQUEST_METHOD(method)]->cnst); - efree(ptr[HTTP_CUSTOM_REQUEST_METHOD(method)]); - ptr[HTTP_CUSTOM_REQUEST_METHOD(method)] = NULL; + unregister_method(*name TSRMLS_CC); + zend_hash_index_del(&HTTP_G->request.methods.registered, method); return SUCCESS; } -/* }}} */ /* * Local variables: diff --git a/package2.xml b/package2.xml index d60ef9a..b27fd1e 100644 --- a/package2.xml +++ b/package2.xml @@ -28,9 +28,9 @@ support. Parallel requests are available for PHP 5 and greater. mike@php.net yes - 2007-02-25 + 2007-05-01 - 1.5.2 + 1.5.3 1.5.0 @@ -39,11 +39,7 @@ support. Parallel requests are available for PHP 5 and greater. BSD, revised setOptions(array("option" => NULL)); +* Fixed crashes with repeated registering/unregisitering of request methods (noticed by Andreas Weber) ]]> diff --git a/php_http.h b/php_http.h index 2ca0d2f..100bd47 100644 --- a/php_http.h +++ b/php_http.h @@ -15,7 +15,7 @@ #ifndef PHP_EXT_HTTP_H #define PHP_EXT_HTTP_H -#define PHP_EXT_HTTP_VERSION "1.5.2" +#define PHP_EXT_HTTP_VERSION "1.5.3dev" #ifdef HAVE_CONFIG_H # include "config.h" @@ -120,12 +120,9 @@ ZEND_BEGIN_MODULE_GLOBALS(http) time_t time; HashTable *headers; struct _http_globals_request_methods { + HashTable registered; char *allowed; - struct _http_globals_request_methods_custom { - char *ini; - int count; - void *entries; - } custom; + char *custom; } methods; #if defined(ZEND_ENGINE_2) && defined(HTTP_HAVE_CURL) struct _http_globals_request_datashare { diff --git a/php_http_request_method_api.h b/php_http_request_method_api.h index e93803a..f867d63 100644 --- a/php_http_request_method_api.h +++ b/php_http_request_method_api.h @@ -54,15 +54,7 @@ typedef enum _http_request_method_t { } http_request_method; #define HTTP_MIN_REQUEST_METHOD (HTTP_NO_REQUEST_METHOD + 1) -#define HTTP_CUSTOM_REQUEST_METHOD_START HTTP_MAX_REQUEST_METHOD - -typedef struct _http_request_method_entry_t { - char *name; - char *cnst; -} http_request_method_entry; - #define HTTP_STD_REQUEST_METHOD(m) ((m > HTTP_NO_REQUEST_METHOD) && (m < HTTP_MAX_REQUEST_METHOD)) -#define HTTP_CUSTOM_REQUEST_METHOD(m) (m - HTTP_MAX_REQUEST_METHOD) extern PHP_MINIT_FUNCTION(http_request_method); extern PHP_RINIT_FUNCTION(http_request_method); @@ -72,7 +64,7 @@ extern PHP_RSHUTDOWN_FUNCTION(http_request_method); PHP_HTTP_API const char *_http_request_method_name(http_request_method m TSRMLS_DC); #define http_request_method_exists(u, l, c) _http_request_method_exists((u), (l), (c) TSRMLS_CC) -PHP_HTTP_API int _http_request_method_exists(zend_bool by_name, http_request_method id, const char *name TSRMLS_DC); +PHP_HTTP_API int _http_request_method_exists(int by_name, http_request_method id, const char *name TSRMLS_DC); #define http_request_method_register(m, l) _http_request_method_register((m), (l) TSRMLS_CC) PHP_HTTP_API int _http_request_method_register(const char *method, int method_name_len TSRMLS_DC); diff --git a/php_http_std_defs.h b/php_http_std_defs.h index b57f714..d20579d 100644 --- a/php_http_std_defs.h +++ b/php_http_std_defs.h @@ -104,6 +104,9 @@ typedef int STATUS; /* max URL length */ #define HTTP_URL_MAXLEN 4096 +/* max request method length */ +#define HTTP_REQUEST_METHOD_MAXLEN 31 + /* def URL arg separator */ #define HTTP_URL_ARGSEP "&" -- 2.30.2