From 446d69890132d7bde48ba0f251ca8f8a86720ee8 Mon Sep 17 00:00:00 2001 From: bugBotter Date: Wed, 25 Jun 2025 10:48:37 -0400 Subject: [PATCH] added --- opcache/zend_accelerator_blacklist.c | 383 +++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 opcache/zend_accelerator_blacklist.c diff --git a/opcache/zend_accelerator_blacklist.c b/opcache/zend_accelerator_blacklist.c new file mode 100644 index 0000000..2a55e07 --- /dev/null +++ b/opcache/zend_accelerator_blacklist.c @@ -0,0 +1,383 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "main/php.h" +#include "main/fopen_wrappers.h" +#include "ZendAccelerator.h" +#include "zend_accelerator_blacklist.h" + +#ifdef ZEND_WIN32 +# define REGEX_MODE (REG_EXTENDED|REG_NOSUB|REG_ICASE) +#else +# define REGEX_MODE (REG_EXTENDED|REG_NOSUB) +#endif + +#ifdef HAVE_GLOB +#ifdef PHP_WIN32 +#include "win32/glob.h" +#else +#include +#endif +#endif + +#include "ext/pcre/php_pcre.h" + +#define ZEND_BLACKLIST_BLOCK_SIZE 32 + +struct _zend_regexp_list { + pcre2_code *re; + zend_regexp_list *next; +}; + +zend_blacklist accel_blacklist; + +void zend_accel_blacklist_init(zend_blacklist *blacklist) +{ + blacklist->pos = 0; + blacklist->size = ZEND_BLACKLIST_BLOCK_SIZE; + + if (blacklist->entries != NULL) { + zend_accel_blacklist_shutdown(blacklist); + } + + blacklist->entries = (zend_blacklist_entry *) calloc(sizeof(zend_blacklist_entry), blacklist->size); + if (!blacklist->entries) { + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Blacklist initialization: no memory\n"); + return; + } + blacklist->regexp_list = NULL; +} + +static void blacklist_report_regexp_error(const char *pcre_error, int pcre_error_offset) +{ + zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Blacklist compilation failed (offset: %d), %s\n", pcre_error_offset, pcre_error); +} + +static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist) +{ + PCRE2_UCHAR pcre_error[128]; + int i, errnumber; + PCRE2_SIZE pcre_error_offset; + zend_regexp_list **regexp_list_it, *it; + char regexp[12*1024], *p, *end, *c, *backtrack = NULL; + pcre2_compile_context *cctx = php_pcre_cctx(); + + if (blacklist->pos == 0) { + /* we have no blacklist to talk about */ + return; + } + + regexp_list_it = &(blacklist->regexp_list); + + regexp[0] = '^'; + regexp[1] = '('; + p = regexp + 2; + end = regexp + sizeof(regexp) - sizeof("[^\\\\]*)\0"); + + for (i = 0; i < blacklist->pos; ) { + c = blacklist->entries[i].path; + if (p + blacklist->entries[i].path_length < end) { + while (*c && p < end) { + switch (*c) { + case '?': + c++; +#ifdef ZEND_WIN32 + p[0] = '['; /* * => [^\\] on Win32 */ + p[1] = '^'; + p[2] = '\\'; + p[3] = '\\'; + p[4] = ']'; + p += 5; +#else + p[0] = '['; /* * => [^/] on *nix */ + p[1] = '^'; + p[2] = '/'; + p[3] = ']'; + p += 4; +#endif + break; + case '*': + c++; + if (*c == '*') { + c++; + p[0] = '.'; /* ** => .* */ + p[1] = '*'; + p += 2; + } else { +#ifdef ZEND_WIN32 + p[0] = '['; /* * => [^\\]* on Win32 */ + p[1] = '^'; + p[2] = '\\'; + p[3] = '\\'; + p[4] = ']'; + p[5] = '*'; + p += 6; +#else + p[0] = '['; /* * => [^/]* on *nix */ + p[1] = '^'; + p[2] = '/'; + p[3] = ']'; + p[4] = '*'; + p += 5; +#endif + } + break; + case '^': + case '.': + case '[': + case ']': + case '$': + case '(': + case ')': + case '|': + case '+': + case '{': + case '}': + case '\\': + *p++ = '\\'; + ZEND_FALLTHROUGH; + default: + *p++ = *c++; + } + } + } + + if (*c || i == blacklist->pos - 1) { + if (*c) { + if (!backtrack) { + zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Too long blacklist entry\n"); + } + p = backtrack; + } else { + i++; + } + *p++ = ')'; + + it = (zend_regexp_list*)malloc(sizeof(zend_regexp_list)); + if (!it) { + zend_accel_error_noreturn(ACCEL_LOG_ERROR, "malloc() failed\n"); + return; + } + it->next = NULL; + + if ((it->re = pcre2_compile((PCRE2_SPTR)regexp, p - regexp, PCRE2_NO_AUTO_CAPTURE, &errnumber, &pcre_error_offset, cctx)) == NULL) { + free(it); + pcre2_get_error_message(errnumber, pcre_error, sizeof(pcre_error)); + blacklist_report_regexp_error((char *)pcre_error, pcre_error_offset); + return; + } +#ifdef HAVE_PCRE_JIT_SUPPORT + if (PCRE_G(jit)) { + if (0 > pcre2_jit_compile(it->re, PCRE2_JIT_COMPLETE)) { + /* Don't return here, even JIT could fail to compile, the pattern is still usable. */ + pcre2_get_error_message(errnumber, pcre_error, sizeof(pcre_error)); + zend_accel_error(ACCEL_LOG_WARNING, "Blacklist JIT compilation failed, %s\n", pcre_error); + } + } +#endif + /* prepare for the next iteration */ + p = regexp + 2; + *regexp_list_it = it; + regexp_list_it = &it->next; + } else { + backtrack = p; + *p++ = '|'; + i++; + } + } +} + +void zend_accel_blacklist_shutdown(zend_blacklist *blacklist) +{ + if (!blacklist->entries) { + return; + } + + zend_blacklist_entry *p = blacklist->entries, *end = blacklist->entries + blacklist->pos; + while (ppath); + p++; + } + free(blacklist->entries); + blacklist->entries = NULL; + if (blacklist->regexp_list) { + zend_regexp_list *temp, *it = blacklist->regexp_list; + while (it) { + pcre2_code_free(it->re); + temp = it; + it = it->next; + free(temp); + } + } +} + +static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist) +{ + if (blacklist->pos == blacklist->size) { + blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE; + blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size); + } +} + +static void zend_accel_blacklist_loadone(zend_blacklist *blacklist, char *filename) +{ + char buf[MAXPATHLEN + 1], real_path[MAXPATHLEN + 1], *blacklist_path = NULL; + FILE *fp; + int path_length, blacklist_path_length; + + if ((fp = fopen(filename, "r")) == NULL) { + zend_accel_error(ACCEL_LOG_WARNING, "Cannot load blacklist file: %s\n", filename); + return; + } + + zend_accel_error(ACCEL_LOG_DEBUG,"Loading blacklist file: '%s'", filename); + + if (VCWD_REALPATH(filename, buf)) { + blacklist_path_length = zend_dirname(buf, strlen(buf)); + blacklist_path = zend_strndup(buf, blacklist_path_length); + } + + memset(buf, 0, sizeof(buf)); + memset(real_path, 0, sizeof(real_path)); + + while (fgets(buf, MAXPATHLEN, fp) != NULL) { + char *path_dup, *pbuf; + path_length = strlen(buf); + if (path_length > 0 && buf[path_length - 1] == '\n') { + buf[--path_length] = 0; + if (path_length > 0 && buf[path_length - 1] == '\r') { + buf[--path_length] = 0; + } + } + + /* Strip ctrl-m prefix */ + pbuf = &buf[0]; + while (*pbuf == '\r') { + *pbuf++ = 0; + path_length--; + } + + /* strip \" */ + if (pbuf[0] == '\"' && pbuf[path_length - 1]== '\"') { + *pbuf++ = 0; + path_length -= 2; + } + + if (path_length == 0) { + continue; + } + + /* skip comments */ + if (pbuf[0]==';') { + continue; + } + + path_dup = zend_strndup(pbuf, path_length); + if (blacklist_path) { + expand_filepath_ex(path_dup, real_path, blacklist_path, blacklist_path_length); + } else { + expand_filepath(path_dup, real_path); + } + path_length = strlen(real_path); + + free(path_dup); + + zend_accel_blacklist_allocate(blacklist); + blacklist->entries[blacklist->pos].path_length = path_length; + blacklist->entries[blacklist->pos].path = (char *)malloc(path_length + 1); + if (!blacklist->entries[blacklist->pos].path) { + zend_accel_error_noreturn(ACCEL_LOG_ERROR, "malloc() failed\n"); + fclose(fp); + return; + } + blacklist->entries[blacklist->pos].id = blacklist->pos; + memcpy(blacklist->entries[blacklist->pos].path, real_path, path_length + 1); + blacklist->pos++; + } + fclose(fp); + if (blacklist_path) { + free(blacklist_path); + } +} + +void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename) +{ +#ifdef HAVE_GLOB + glob_t globbuf; + int ret; + unsigned int i; + + memset(&globbuf, 0, sizeof(glob_t)); + + ret = glob(filename, 0, NULL, &globbuf); +#ifdef GLOB_NOMATCH + if (ret == GLOB_NOMATCH || !globbuf.gl_pathc) { +#else + if (!globbuf.gl_pathc) { +#endif + zend_accel_error(ACCEL_LOG_WARNING, "No blacklist file found matching: %s\n", filename); + } else { + for(i=0 ; iregexp_list; + pcre2_match_context *mctx = php_pcre_mctx(); + + if (regexp_list_it == NULL) { + return 0; + } + while (regexp_list_it != NULL) { + pcre2_match_data *match_data = php_pcre_create_match_data(0, regexp_list_it->re); + if (!match_data) { + /* Alloc failed, but next one could still come through and match. */ + continue; + } + int rc = pcre2_match(regexp_list_it->re, (PCRE2_SPTR)verify_path, verify_path_len, 0, 0, match_data, mctx); + if (rc >= 0) { + ret = 1; + php_pcre_free_match_data(match_data); + break; + } + php_pcre_free_match_data(match_data); + regexp_list_it = regexp_list_it->next; + } + return ret; +} + +void zend_accel_blacklist_apply(zend_blacklist *blacklist, blacklist_apply_func_arg_t func, void *argument) +{ + int i; + + for (i = 0; i < blacklist->pos; i++) { + func(&blacklist->entries[i], argument); + } +}