From 99a208566adfa32bf3a759c8731755f5e2458fdd Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Fri, 21 May 2021 20:35:54 +0200 Subject: [PATCH] Fix bug #81068: Fix possible use-after-free in realpath_cache_clean() If ZTS is enabled, this can cause cwd_globals_ctor() to be called multiple times, each with a freshly allocated virtual_cwd_globals instance. At shutdown time however, cwd_globals_dtor() will call realpath_cache_clean(), which then possibly cleans up the same realpath_cache instance more than once. Using AddressSanitzer, this shows up as a heap use-after-free. To avoid this, add a helper function to do the actual work on one instance of a realpath_cache, and call it both from cwd_globals_dtor() and realpath_cache_clean(). The former uses the virtual_cwd_globals parameter passed in via the destructor, the latter uses the CWDG() macro. --- NEWS | 3 +++ Zend/zend_virtual_cwd.c | 31 ++++++++++++++++++------------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/NEWS b/NEWS index 0ae36ce99b7..847a9af27f4 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 7.4.21 +- Core: + . Fixed bug #81068 (Double free in realpath_cache_clean()). (Dimitry Andric) + - Standard: . Fixed bug #81048 (phpinfo(INFO_VARIABLES) "Array to string conversion"). (cmb) diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c index 7b0d05d63d6..e16ba50f135 100644 --- a/Zend/zend_virtual_cwd.c +++ b/Zend/zend_virtual_cwd.c @@ -156,9 +156,25 @@ static void cwd_globals_ctor(virtual_cwd_globals *cwd_g) /* {{{ */ } /* }}} */ +static void realpath_cache_clean_helper(uint32_t max_entries, realpath_cache_bucket **cache, zend_long *cache_size) +{ + uint32_t i; + + for (i = 0; i < max_entries; i++) { + realpath_cache_bucket *p = cache[i]; + while (p != NULL) { + realpath_cache_bucket *r = p; + p = p->next; + free(r); + } + cache[i] = NULL; + } + *cache_size = 0; +} + static void cwd_globals_dtor(virtual_cwd_globals *cwd_g) /* {{{ */ { - realpath_cache_clean(); + realpath_cache_clean_helper(sizeof(cwd_g->realpath_cache)/sizeof(cwd_g->realpath_cache[0]), cwd_g->realpath_cache, &cwd_g->realpath_cache_size); } /* }}} */ @@ -346,18 +362,7 @@ static inline zend_ulong realpath_cache_key(const char *path, size_t path_len) / CWD_API void realpath_cache_clean(void) /* {{{ */ { - uint32_t i; - - for (i = 0; i < sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]); i++) { - realpath_cache_bucket *p = CWDG(realpath_cache)[i]; - while (p != NULL) { - realpath_cache_bucket *r = p; - p = p->next; - free(r); - } - CWDG(realpath_cache)[i] = NULL; - } - CWDG(realpath_cache_size) = 0; + realpath_cache_clean_helper(sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]), CWDG(realpath_cache), &CWDG(realpath_cache_size)); } /* }}} */