PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/ext/opcache/zend_accelerator_blacklist.c

http://github.com/php/php-src
C | 378 lines | 308 code | 42 blank | 28 comment | 65 complexity | 7a83e13784834dcdbb24ba54bec18d0a MD5 | raw file
Possible License(s): BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend OPcache |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Andi Gutmans <andi@php.net> |
  16. | Zeev Suraski <zeev@php.net> |
  17. | Stanislav Malyshev <stas@zend.com> |
  18. | Dmitry Stogov <dmitry@php.net> |
  19. +----------------------------------------------------------------------+
  20. */
  21. #include "main/php.h"
  22. #include "main/fopen_wrappers.h"
  23. #include "ZendAccelerator.h"
  24. #include "zend_accelerator_blacklist.h"
  25. #ifdef ZEND_WIN32
  26. # define REGEX_MODE (REG_EXTENDED|REG_NOSUB|REG_ICASE)
  27. #else
  28. # define REGEX_MODE (REG_EXTENDED|REG_NOSUB)
  29. #endif
  30. #ifdef HAVE_GLOB
  31. #ifdef PHP_WIN32
  32. #include "win32/glob.h"
  33. #else
  34. #include <glob.h>
  35. #endif
  36. #endif
  37. #include "ext/pcre/php_pcre.h"
  38. #define ZEND_BLACKLIST_BLOCK_SIZE 32
  39. struct _zend_regexp_list {
  40. pcre2_code *re;
  41. zend_regexp_list *next;
  42. };
  43. zend_blacklist accel_blacklist;
  44. void zend_accel_blacklist_init(zend_blacklist *blacklist)
  45. {
  46. blacklist->pos = 0;
  47. blacklist->size = ZEND_BLACKLIST_BLOCK_SIZE;
  48. if (blacklist->entries != NULL) {
  49. zend_accel_blacklist_shutdown(blacklist);
  50. }
  51. blacklist->entries = (zend_blacklist_entry *) calloc(sizeof(zend_blacklist_entry), blacklist->size);
  52. if (!blacklist->entries) {
  53. zend_accel_error(ACCEL_LOG_FATAL, "Blacklist initialization: no memory\n");
  54. return;
  55. }
  56. blacklist->regexp_list = NULL;
  57. }
  58. static void blacklist_report_regexp_error(const char *pcre_error, int pcre_error_offset)
  59. {
  60. zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation failed (offset: %d), %s\n", pcre_error_offset, pcre_error);
  61. }
  62. static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist)
  63. {
  64. PCRE2_UCHAR pcre_error[128];
  65. int i, errnumber;
  66. PCRE2_SIZE pcre_error_offset;
  67. zend_regexp_list **regexp_list_it, *it;
  68. char regexp[12*1024], *p, *end, *c, *backtrack = NULL;
  69. pcre2_compile_context *cctx = php_pcre_cctx();
  70. if (blacklist->pos == 0) {
  71. /* we have no blacklist to talk about */
  72. return;
  73. }
  74. regexp_list_it = &(blacklist->regexp_list);
  75. regexp[0] = '^';
  76. regexp[1] = '(';
  77. p = regexp + 2;
  78. end = regexp + sizeof(regexp) - sizeof("[^\\\\]*)\0");
  79. for (i = 0; i < blacklist->pos; ) {
  80. c = blacklist->entries[i].path;
  81. if (p + blacklist->entries[i].path_length < end) {
  82. while (*c && p < end) {
  83. switch (*c) {
  84. case '?':
  85. c++;
  86. #ifdef ZEND_WIN32
  87. p[0] = '['; /* * => [^\\] on Win32 */
  88. p[1] = '^';
  89. p[2] = '\\';
  90. p[3] = '\\';
  91. p[4] = ']';
  92. p += 5;
  93. #else
  94. p[0] = '['; /* * => [^/] on *nix */
  95. p[1] = '^';
  96. p[2] = '/';
  97. p[3] = ']';
  98. p += 4;
  99. #endif
  100. break;
  101. case '*':
  102. c++;
  103. if (*c == '*') {
  104. c++;
  105. p[0] = '.'; /* ** => .* */
  106. p[1] = '*';
  107. p += 2;
  108. } else {
  109. #ifdef ZEND_WIN32
  110. p[0] = '['; /* * => [^\\]* on Win32 */
  111. p[1] = '^';
  112. p[2] = '\\';
  113. p[3] = '\\';
  114. p[4] = ']';
  115. p[5] = '*';
  116. p += 6;
  117. #else
  118. p[0] = '['; /* * => [^/]* on *nix */
  119. p[1] = '^';
  120. p[2] = '/';
  121. p[3] = ']';
  122. p[4] = '*';
  123. p += 5;
  124. #endif
  125. }
  126. break;
  127. case '^':
  128. case '.':
  129. case '[':
  130. case ']':
  131. case '$':
  132. case '(':
  133. case ')':
  134. case '|':
  135. case '+':
  136. case '{':
  137. case '}':
  138. case '\\':
  139. *p++ = '\\';
  140. /* break missing intentionally */
  141. default:
  142. *p++ = *c++;
  143. }
  144. }
  145. }
  146. if (*c || i == blacklist->pos - 1) {
  147. if (*c) {
  148. if (!backtrack) {
  149. zend_accel_error(ACCEL_LOG_ERROR, "Too long blacklist entry\n");
  150. }
  151. p = backtrack;
  152. } else {
  153. i++;
  154. }
  155. *p++ = ')';
  156. it = (zend_regexp_list*)malloc(sizeof(zend_regexp_list));
  157. if (!it) {
  158. zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n");
  159. return;
  160. }
  161. it->next = NULL;
  162. if ((it->re = pcre2_compile((PCRE2_SPTR)regexp, p - regexp, PCRE2_NO_AUTO_CAPTURE, &errnumber, &pcre_error_offset, cctx)) == NULL) {
  163. free(it);
  164. pcre2_get_error_message(errnumber, pcre_error, sizeof(pcre_error));
  165. blacklist_report_regexp_error((char *)pcre_error, pcre_error_offset);
  166. return;
  167. }
  168. #ifdef HAVE_PCRE_JIT_SUPPORT
  169. if (0 > pcre2_jit_compile(it->re, PCRE2_JIT_COMPLETE)) {
  170. /* Don't return here, even JIT could fail to compile, the pattern is still usable. */
  171. pcre2_get_error_message(errnumber, pcre_error, sizeof(pcre_error));
  172. zend_accel_error(ACCEL_LOG_WARNING, "Blacklist JIT compilation failed, %s\n", pcre_error);
  173. }
  174. #endif
  175. /* prepare for the next iteration */
  176. p = regexp + 2;
  177. *regexp_list_it = it;
  178. regexp_list_it = &it->next;
  179. } else {
  180. backtrack = p;
  181. *p++ = '|';
  182. i++;
  183. }
  184. }
  185. }
  186. void zend_accel_blacklist_shutdown(zend_blacklist *blacklist)
  187. {
  188. zend_blacklist_entry *p = blacklist->entries, *end = blacklist->entries + blacklist->pos;
  189. while (p<end) {
  190. free(p->path);
  191. p++;
  192. }
  193. free(blacklist->entries);
  194. blacklist->entries = NULL;
  195. if (blacklist->regexp_list) {
  196. zend_regexp_list *temp, *it = blacklist->regexp_list;
  197. while (it) {
  198. pcre2_code_free(it->re);
  199. temp = it;
  200. it = it->next;
  201. free(temp);
  202. }
  203. }
  204. }
  205. static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist)
  206. {
  207. if (blacklist->pos == blacklist->size) {
  208. blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE;
  209. blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size);
  210. }
  211. }
  212. static void zend_accel_blacklist_loadone(zend_blacklist *blacklist, char *filename)
  213. {
  214. char buf[MAXPATHLEN + 1], real_path[MAXPATHLEN + 1], *blacklist_path = NULL;
  215. FILE *fp;
  216. int path_length, blacklist_path_length;
  217. if ((fp = fopen(filename, "r")) == NULL) {
  218. zend_accel_error(ACCEL_LOG_WARNING, "Cannot load blacklist file: %s\n", filename);
  219. return;
  220. }
  221. zend_accel_error(ACCEL_LOG_DEBUG,"Loading blacklist file: '%s'", filename);
  222. if (VCWD_REALPATH(filename, buf)) {
  223. blacklist_path_length = zend_dirname(buf, strlen(buf));
  224. blacklist_path = zend_strndup(buf, blacklist_path_length);
  225. }
  226. memset(buf, 0, sizeof(buf));
  227. memset(real_path, 0, sizeof(real_path));
  228. while (fgets(buf, MAXPATHLEN, fp) != NULL) {
  229. char *path_dup, *pbuf;
  230. path_length = strlen(buf);
  231. if (path_length > 0 && buf[path_length - 1] == '\n') {
  232. buf[--path_length] = 0;
  233. if (path_length > 0 && buf[path_length - 1] == '\r') {
  234. buf[--path_length] = 0;
  235. }
  236. }
  237. /* Strip ctrl-m prefix */
  238. pbuf = &buf[0];
  239. while (*pbuf == '\r') {
  240. *pbuf++ = 0;
  241. path_length--;
  242. }
  243. /* strip \" */
  244. if (pbuf[0] == '\"' && pbuf[path_length - 1]== '\"') {
  245. *pbuf++ = 0;
  246. path_length -= 2;
  247. }
  248. if (path_length == 0) {
  249. continue;
  250. }
  251. /* skip comments */
  252. if (pbuf[0]==';') {
  253. continue;
  254. }
  255. path_dup = zend_strndup(pbuf, path_length);
  256. if (blacklist_path) {
  257. expand_filepath_ex(path_dup, real_path, blacklist_path, blacklist_path_length);
  258. } else {
  259. expand_filepath(path_dup, real_path);
  260. }
  261. path_length = strlen(real_path);
  262. free(path_dup);
  263. zend_accel_blacklist_allocate(blacklist);
  264. blacklist->entries[blacklist->pos].path_length = path_length;
  265. blacklist->entries[blacklist->pos].path = (char *)malloc(path_length + 1);
  266. if (!blacklist->entries[blacklist->pos].path) {
  267. zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n");
  268. fclose(fp);
  269. return;
  270. }
  271. blacklist->entries[blacklist->pos].id = blacklist->pos;
  272. memcpy(blacklist->entries[blacklist->pos].path, real_path, path_length + 1);
  273. blacklist->pos++;
  274. }
  275. fclose(fp);
  276. if (blacklist_path) {
  277. free(blacklist_path);
  278. }
  279. }
  280. void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename)
  281. {
  282. #ifdef HAVE_GLOB
  283. glob_t globbuf;
  284. int ret;
  285. unsigned int i;
  286. memset(&globbuf, 0, sizeof(glob_t));
  287. ret = glob(filename, 0, NULL, &globbuf);
  288. #ifdef GLOB_NOMATCH
  289. if (ret == GLOB_NOMATCH || !globbuf.gl_pathc) {
  290. #else
  291. if (!globbuf.gl_pathc) {
  292. #endif
  293. zend_accel_error(ACCEL_LOG_WARNING, "No blacklist file found matching: %s\n", filename);
  294. } else {
  295. for(i=0 ; i<globbuf.gl_pathc; i++) {
  296. zend_accel_blacklist_loadone(blacklist, globbuf.gl_pathv[i]);
  297. }
  298. globfree(&globbuf);
  299. }
  300. #else
  301. zend_accel_blacklist_loadone(blacklist, filename);
  302. #endif
  303. zend_accel_blacklist_update_regexp(blacklist);
  304. }
  305. zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path, size_t verify_path_len)
  306. {
  307. int ret = 0;
  308. zend_regexp_list *regexp_list_it = blacklist->regexp_list;
  309. pcre2_match_context *mctx = php_pcre_mctx();
  310. if (regexp_list_it == NULL) {
  311. return 0;
  312. }
  313. while (regexp_list_it != NULL) {
  314. pcre2_match_data *match_data = php_pcre_create_match_data(0, regexp_list_it->re);
  315. if (!match_data) {
  316. /* Alloc failed, but next one could still come through and match. */
  317. continue;
  318. }
  319. int rc = pcre2_match(regexp_list_it->re, (PCRE2_SPTR)verify_path, verify_path_len, 0, 0, match_data, mctx);
  320. if (rc >= 0) {
  321. ret = 1;
  322. php_pcre_free_match_data(match_data);
  323. break;
  324. }
  325. php_pcre_free_match_data(match_data);
  326. regexp_list_it = regexp_list_it->next;
  327. }
  328. return ret;
  329. }
  330. void zend_accel_blacklist_apply(zend_blacklist *blacklist, blacklist_apply_func_arg_t func, void *argument)
  331. {
  332. int i;
  333. for (i = 0; i < blacklist->pos; i++) {
  334. func(&blacklist->entries[i], argument);
  335. }
  336. }