/src/php-src/sapi/fuzzer/fuzzer-execute-common.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright (c) The PHP Group | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to version 3.01 of the PHP license, | |
6 | | | that is bundled with this package in the file LICENSE, and is | |
7 | | | available through the world-wide-web at the following url: | |
8 | | | https://www.php.net/license/3_01.txt | |
9 | | | If you did not receive a copy of the PHP license and are unable to | |
10 | | | obtain it through the world-wide-web, please send a note to | |
11 | | | license@php.net so we can mail you a copy immediately. | |
12 | | +----------------------------------------------------------------------+ |
13 | | | Authors: Nikita Popov <nikic@php.net> | |
14 | | +----------------------------------------------------------------------+ |
15 | | */ |
16 | | |
17 | | #include <main/php.h> |
18 | | |
19 | | #if defined(__FreeBSD__) |
20 | | # include <sys/sysctl.h> |
21 | | #endif |
22 | | |
23 | | #include "fuzzer.h" |
24 | | #include "fuzzer-sapi.h" |
25 | | #include "zend_exceptions.h" |
26 | | |
27 | 260k | #define FILE_NAME "/tmp/fuzzer.php" |
28 | 317k | #define MAX_STEPS 1000 |
29 | 1.11M | #define MAX_SIZE (8 * 1024) |
30 | 13.6M | #define ZEND_VM_ENTER_BIT 1ULL |
31 | | |
32 | | static uint32_t steps_left; |
33 | | static bool bailed_out = false; |
34 | | |
35 | | /* Because the fuzzer is always compiled with clang, |
36 | | * we can assume that we don't use global registers / hybrid VM. */ |
37 | | typedef zend_op *(ZEND_FASTCALL *opcode_handler_t)(zend_execute_data *, const zend_op *); |
38 | | |
39 | 18.5k | static zend_always_inline void fuzzer_bailout(void) { |
40 | 18.5k | bailed_out = true; |
41 | 18.5k | zend_bailout(); |
42 | 18.5k | } fuzzer-tracing-jit.c:fuzzer_bailout Line | Count | Source | 39 | 6.95k | static zend_always_inline void fuzzer_bailout(void) { | 40 | 6.95k | bailed_out = true; | 41 | 6.95k | zend_bailout(); | 42 | 6.95k | } |
fuzzer-function-jit.c:fuzzer_bailout Line | Count | Source | 39 | 7.84k | static zend_always_inline void fuzzer_bailout(void) { | 40 | 7.84k | bailed_out = true; | 41 | 7.84k | zend_bailout(); | 42 | 7.84k | } |
fuzzer-execute.c:fuzzer_bailout Line | Count | Source | 39 | 3.75k | static zend_always_inline void fuzzer_bailout(void) { | 40 | 3.75k | bailed_out = true; | 41 | 3.75k | zend_bailout(); | 42 | 3.75k | } |
|
43 | | |
44 | 14.8M | static zend_always_inline void fuzzer_step(void) { |
45 | 14.8M | if (--steps_left == 0) { |
46 | | /* Reset steps before bailing out, so code running after bailout (e.g. in |
47 | | * destructors) will get another MAX_STEPS, rather than UINT32_MAX steps. */ |
48 | 7.09k | steps_left = MAX_STEPS; |
49 | 7.09k | fuzzer_bailout(); |
50 | 7.09k | } |
51 | 14.8M | } fuzzer-tracing-jit.c:fuzzer_step Line | Count | Source | 44 | 5.73M | static zend_always_inline void fuzzer_step(void) { | 45 | 5.73M | if (--steps_left == 0) { | 46 | | /* Reset steps before bailing out, so code running after bailout (e.g. in | 47 | | * destructors) will get another MAX_STEPS, rather than UINT32_MAX steps. */ | 48 | 2.35k | steps_left = MAX_STEPS; | 49 | 2.35k | fuzzer_bailout(); | 50 | 2.35k | } | 51 | 5.73M | } |
fuzzer-function-jit.c:fuzzer_step Line | Count | Source | 44 | 6.38M | static zend_always_inline void fuzzer_step(void) { | 45 | 6.38M | if (--steps_left == 0) { | 46 | | /* Reset steps before bailing out, so code running after bailout (e.g. in | 47 | | * destructors) will get another MAX_STEPS, rather than UINT32_MAX steps. */ | 48 | 2.68k | steps_left = MAX_STEPS; | 49 | 2.68k | fuzzer_bailout(); | 50 | 2.68k | } | 51 | 6.38M | } |
fuzzer-execute.c:fuzzer_step Line | Count | Source | 44 | 2.73M | static zend_always_inline void fuzzer_step(void) { | 45 | 2.73M | if (--steps_left == 0) { | 46 | | /* Reset steps before bailing out, so code running after bailout (e.g. in | 47 | | * destructors) will get another MAX_STEPS, rather than UINT32_MAX steps. */ | 48 | 2.05k | steps_left = MAX_STEPS; | 49 | 2.05k | fuzzer_bailout(); | 50 | 2.05k | } | 51 | 2.73M | } |
|
52 | | |
53 | | static void (*orig_execute_ex)(zend_execute_data *execute_data); |
54 | | |
55 | 433k | static void fuzzer_execute_ex(zend_execute_data *execute_data) { |
56 | 433k | const zend_op *opline = EX(opline); |
57 | 13.9M | while (1) { |
58 | 13.5M | fuzzer_step(); |
59 | 13.5M | opline = ((opcode_handler_t) opline->handler)(execute_data, opline); |
60 | 13.5M | if ((uintptr_t) opline & ZEND_VM_ENTER_BIT) { |
61 | 81.8k | opline = (const zend_op *) ((uintptr_t) opline & ~ZEND_VM_ENTER_BIT); |
62 | 81.8k | if (opline) { |
63 | 0 | execute_data = EG(current_execute_data); |
64 | 81.8k | } else { |
65 | 81.8k | return; |
66 | 81.8k | } |
67 | 81.8k | } |
68 | 13.5M | } |
69 | 433k | } fuzzer-tracing-jit.c:fuzzer_execute_ex Line | Count | Source | 55 | 157k | static void fuzzer_execute_ex(zend_execute_data *execute_data) { | 56 | 157k | const zend_op *opline = EX(opline); | 57 | 5.29M | while (1) { | 58 | 5.17M | fuzzer_step(); | 59 | 5.17M | opline = ((opcode_handler_t) opline->handler)(execute_data, opline); | 60 | 5.17M | if ((uintptr_t) opline & ZEND_VM_ENTER_BIT) { | 61 | 35.6k | opline = (const zend_op *) ((uintptr_t) opline & ~ZEND_VM_ENTER_BIT); | 62 | 35.6k | if (opline) { | 63 | 0 | execute_data = EG(current_execute_data); | 64 | 35.6k | } else { | 65 | 35.6k | return; | 66 | 35.6k | } | 67 | 35.6k | } | 68 | 5.17M | } | 69 | 157k | } |
fuzzer-function-jit.c:fuzzer_execute_ex Line | Count | Source | 55 | 170k | static void fuzzer_execute_ex(zend_execute_data *execute_data) { | 56 | 170k | const zend_op *opline = EX(opline); | 57 | 5.94M | while (1) { | 58 | 5.81M | fuzzer_step(); | 59 | 5.81M | opline = ((opcode_handler_t) opline->handler)(execute_data, opline); | 60 | 5.81M | if ((uintptr_t) opline & ZEND_VM_ENTER_BIT) { | 61 | 43.0k | opline = (const zend_op *) ((uintptr_t) opline & ~ZEND_VM_ENTER_BIT); | 62 | 43.0k | if (opline) { | 63 | 0 | execute_data = EG(current_execute_data); | 64 | 43.0k | } else { | 65 | 43.0k | return; | 66 | 43.0k | } | 67 | 43.0k | } | 68 | 5.81M | } | 69 | 170k | } |
fuzzer-execute.c:fuzzer_execute_ex Line | Count | Source | 55 | 105k | static void fuzzer_execute_ex(zend_execute_data *execute_data) { | 56 | 105k | const zend_op *opline = EX(opline); | 57 | 2.70M | while (1) { | 58 | 2.60M | fuzzer_step(); | 59 | 2.60M | opline = ((opcode_handler_t) opline->handler)(execute_data, opline); | 60 | 2.60M | if ((uintptr_t) opline & ZEND_VM_ENTER_BIT) { | 61 | 3.11k | opline = (const zend_op *) ((uintptr_t) opline & ~ZEND_VM_ENTER_BIT); | 62 | 3.11k | if (opline) { | 63 | 0 | execute_data = EG(current_execute_data); | 64 | 3.11k | } else { | 65 | 3.11k | return; | 66 | 3.11k | } | 67 | 3.11k | } | 68 | 2.60M | } | 69 | 105k | } |
|
70 | | |
71 | | static zend_op_array *(*orig_compile_string)( |
72 | | zend_string *source_string, const char *filename, zend_compile_position position); |
73 | | |
74 | | static zend_op_array *fuzzer_compile_string( |
75 | 4.23k | zend_string *str, const char *filename, zend_compile_position position) { |
76 | 4.23k | if (ZSTR_LEN(str) > MAX_SIZE) { |
77 | | /* Avoid compiling huge inputs via eval(). */ |
78 | 8 | fuzzer_bailout(); |
79 | 8 | } |
80 | | |
81 | 4.23k | return orig_compile_string(str, filename, position); |
82 | 4.23k | } fuzzer-tracing-jit.c:fuzzer_compile_string Line | Count | Source | 75 | 2.19k | zend_string *str, const char *filename, zend_compile_position position) { | 76 | 2.19k | if (ZSTR_LEN(str) > MAX_SIZE) { | 77 | | /* Avoid compiling huge inputs via eval(). */ | 78 | 2 | fuzzer_bailout(); | 79 | 2 | } | 80 | | | 81 | 2.19k | return orig_compile_string(str, filename, position); | 82 | 2.19k | } |
fuzzer-function-jit.c:fuzzer_compile_string Line | Count | Source | 75 | 1.98k | zend_string *str, const char *filename, zend_compile_position position) { | 76 | 1.98k | if (ZSTR_LEN(str) > MAX_SIZE) { | 77 | | /* Avoid compiling huge inputs via eval(). */ | 78 | 6 | fuzzer_bailout(); | 79 | 6 | } | 80 | | | 81 | 1.98k | return orig_compile_string(str, filename, position); | 82 | 1.98k | } |
fuzzer-execute.c:fuzzer_compile_string Line | Count | Source | 75 | 60 | zend_string *str, const char *filename, zend_compile_position position) { | 76 | 60 | if (ZSTR_LEN(str) > MAX_SIZE) { | 77 | | /* Avoid compiling huge inputs via eval(). */ | 78 | 0 | fuzzer_bailout(); | 79 | 0 | } | 80 | | | 81 | 60 | return orig_compile_string(str, filename, position); | 82 | 60 | } |
|
83 | | |
84 | | static void (*orig_execute_internal)(zend_execute_data *execute_data, zval *return_value); |
85 | | |
86 | 1.25M | static void fuzzer_execute_internal(zend_execute_data *execute_data, zval *return_value) { |
87 | 1.25M | fuzzer_step(); |
88 | | |
89 | 1.25M | uint32_t num_args = ZEND_CALL_NUM_ARGS(execute_data); |
90 | 3.03M | for (uint32_t i = 0; i < num_args; i++) { |
91 | | /* Some internal functions like preg_replace() may be slow on large inputs. |
92 | | * Limit the maximum size of string inputs. */ |
93 | 1.77M | zval *arg = ZEND_CALL_VAR_NUM(execute_data, i); |
94 | 1.77M | if (Z_TYPE_P(arg) == IS_STRING && Z_STRLEN_P(arg) > MAX_SIZE) { |
95 | 11.4k | fuzzer_bailout(); |
96 | 11.4k | } |
97 | 1.77M | } |
98 | | |
99 | 1.25M | orig_execute_internal(execute_data, return_value); |
100 | 1.25M | } fuzzer-tracing-jit.c:fuzzer_execute_internal Line | Count | Source | 86 | 563k | static void fuzzer_execute_internal(zend_execute_data *execute_data, zval *return_value) { | 87 | 563k | fuzzer_step(); | 88 | | | 89 | 563k | uint32_t num_args = ZEND_CALL_NUM_ARGS(execute_data); | 90 | 1.33M | for (uint32_t i = 0; i < num_args; i++) { | 91 | | /* Some internal functions like preg_replace() may be slow on large inputs. | 92 | | * Limit the maximum size of string inputs. */ | 93 | 769k | zval *arg = ZEND_CALL_VAR_NUM(execute_data, i); | 94 | 769k | if (Z_TYPE_P(arg) == IS_STRING && Z_STRLEN_P(arg) > MAX_SIZE) { | 95 | 4.60k | fuzzer_bailout(); | 96 | 4.60k | } | 97 | 769k | } | 98 | | | 99 | 563k | orig_execute_internal(execute_data, return_value); | 100 | 563k | } |
fuzzer-function-jit.c:fuzzer_execute_internal Line | Count | Source | 86 | 565k | static void fuzzer_execute_internal(zend_execute_data *execute_data, zval *return_value) { | 87 | 565k | fuzzer_step(); | 88 | | | 89 | 565k | uint32_t num_args = ZEND_CALL_NUM_ARGS(execute_data); | 90 | 1.32M | for (uint32_t i = 0; i < num_args; i++) { | 91 | | /* Some internal functions like preg_replace() may be slow on large inputs. | 92 | | * Limit the maximum size of string inputs. */ | 93 | 762k | zval *arg = ZEND_CALL_VAR_NUM(execute_data, i); | 94 | 762k | if (Z_TYPE_P(arg) == IS_STRING && Z_STRLEN_P(arg) > MAX_SIZE) { | 95 | 5.15k | fuzzer_bailout(); | 96 | 5.15k | } | 97 | 762k | } | 98 | | | 99 | 565k | orig_execute_internal(execute_data, return_value); | 100 | 565k | } |
fuzzer-execute.c:fuzzer_execute_internal Line | Count | Source | 86 | 129k | static void fuzzer_execute_internal(zend_execute_data *execute_data, zval *return_value) { | 87 | 129k | fuzzer_step(); | 88 | | | 89 | 129k | uint32_t num_args = ZEND_CALL_NUM_ARGS(execute_data); | 90 | 376k | for (uint32_t i = 0; i < num_args; i++) { | 91 | | /* Some internal functions like preg_replace() may be slow on large inputs. | 92 | | * Limit the maximum size of string inputs. */ | 93 | 247k | zval *arg = ZEND_CALL_VAR_NUM(execute_data, i); | 94 | 247k | if (Z_TYPE_P(arg) == IS_STRING && Z_STRLEN_P(arg) > MAX_SIZE) { | 95 | 1.69k | fuzzer_bailout(); | 96 | 1.69k | } | 97 | 247k | } | 98 | | | 99 | 129k | orig_execute_internal(execute_data, return_value); | 100 | 129k | } |
|
101 | | |
102 | 6 | static void fuzzer_init_php_for_execute(const char *extra_ini) { |
103 | | /* Compilation will often trigger fatal errors. |
104 | | * Use tracked allocation mode to avoid leaks in that case. */ |
105 | 6 | putenv("USE_TRACKED_ALLOC=1"); |
106 | | |
107 | | /* Just like other SAPIs, ignore SIGPIPEs. */ |
108 | 6 | signal(SIGPIPE, SIG_IGN); |
109 | | |
110 | 6 | fuzzer_init_php(extra_ini); |
111 | | |
112 | 6 | orig_execute_ex = zend_execute_ex; |
113 | 6 | zend_execute_ex = fuzzer_execute_ex; |
114 | 6 | orig_execute_internal = zend_execute_internal ? zend_execute_internal : execute_internal; |
115 | 6 | zend_execute_internal = fuzzer_execute_internal; |
116 | 6 | orig_compile_string = zend_compile_string; |
117 | 6 | zend_compile_string = fuzzer_compile_string; |
118 | 6 | } fuzzer-tracing-jit.c:fuzzer_init_php_for_execute Line | Count | Source | 102 | 2 | static void fuzzer_init_php_for_execute(const char *extra_ini) { | 103 | | /* Compilation will often trigger fatal errors. | 104 | | * Use tracked allocation mode to avoid leaks in that case. */ | 105 | 2 | putenv("USE_TRACKED_ALLOC=1"); | 106 | | | 107 | | /* Just like other SAPIs, ignore SIGPIPEs. */ | 108 | 2 | signal(SIGPIPE, SIG_IGN); | 109 | | | 110 | 2 | fuzzer_init_php(extra_ini); | 111 | | | 112 | 2 | orig_execute_ex = zend_execute_ex; | 113 | 2 | zend_execute_ex = fuzzer_execute_ex; | 114 | 2 | orig_execute_internal = zend_execute_internal ? zend_execute_internal : execute_internal; | 115 | 2 | zend_execute_internal = fuzzer_execute_internal; | 116 | 2 | orig_compile_string = zend_compile_string; | 117 | 2 | zend_compile_string = fuzzer_compile_string; | 118 | 2 | } |
fuzzer-function-jit.c:fuzzer_init_php_for_execute Line | Count | Source | 102 | 2 | static void fuzzer_init_php_for_execute(const char *extra_ini) { | 103 | | /* Compilation will often trigger fatal errors. | 104 | | * Use tracked allocation mode to avoid leaks in that case. */ | 105 | 2 | putenv("USE_TRACKED_ALLOC=1"); | 106 | | | 107 | | /* Just like other SAPIs, ignore SIGPIPEs. */ | 108 | 2 | signal(SIGPIPE, SIG_IGN); | 109 | | | 110 | 2 | fuzzer_init_php(extra_ini); | 111 | | | 112 | 2 | orig_execute_ex = zend_execute_ex; | 113 | 2 | zend_execute_ex = fuzzer_execute_ex; | 114 | 2 | orig_execute_internal = zend_execute_internal ? zend_execute_internal : execute_internal; | 115 | 2 | zend_execute_internal = fuzzer_execute_internal; | 116 | 2 | orig_compile_string = zend_compile_string; | 117 | 2 | zend_compile_string = fuzzer_compile_string; | 118 | 2 | } |
fuzzer-execute.c:fuzzer_init_php_for_execute Line | Count | Source | 102 | 2 | static void fuzzer_init_php_for_execute(const char *extra_ini) { | 103 | | /* Compilation will often trigger fatal errors. | 104 | | * Use tracked allocation mode to avoid leaks in that case. */ | 105 | 2 | putenv("USE_TRACKED_ALLOC=1"); | 106 | | | 107 | | /* Just like other SAPIs, ignore SIGPIPEs. */ | 108 | 2 | signal(SIGPIPE, SIG_IGN); | 109 | | | 110 | 2 | fuzzer_init_php(extra_ini); | 111 | | | 112 | 2 | orig_execute_ex = zend_execute_ex; | 113 | 2 | zend_execute_ex = fuzzer_execute_ex; | 114 | 2 | orig_execute_internal = zend_execute_internal ? zend_execute_internal : execute_internal; | 115 | 2 | zend_execute_internal = fuzzer_execute_internal; | 116 | 2 | orig_compile_string = zend_compile_string; | 117 | 2 | zend_compile_string = fuzzer_compile_string; | 118 | 2 | } |
|
119 | | |
120 | 4 | ZEND_ATTRIBUTE_UNUSED static void create_file(void) { |
121 | | /* For opcache_invalidate() to work, the dummy file name used for fuzzing needs to |
122 | | * actually exist. */ |
123 | 4 | FILE *f = fopen(FILE_NAME, "w"); |
124 | 4 | fclose(f); |
125 | 4 | } fuzzer-tracing-jit.c:create_file Line | Count | Source | 120 | 2 | ZEND_ATTRIBUTE_UNUSED static void create_file(void) { | 121 | | /* For opcache_invalidate() to work, the dummy file name used for fuzzing needs to | 122 | | * actually exist. */ | 123 | 2 | FILE *f = fopen(FILE_NAME, "w"); | 124 | 2 | fclose(f); | 125 | 2 | } |
fuzzer-function-jit.c:create_file Line | Count | Source | 120 | 2 | ZEND_ATTRIBUTE_UNUSED static void create_file(void) { | 121 | | /* For opcache_invalidate() to work, the dummy file name used for fuzzing needs to | 122 | | * actually exist. */ | 123 | 2 | FILE *f = fopen(FILE_NAME, "w"); | 124 | 2 | fclose(f); | 125 | 2 | } |
Unexecuted instantiation: fuzzer-execute.c:create_file |
126 | | |
127 | 86.0k | ZEND_ATTRIBUTE_UNUSED static void opcache_invalidate(void) { |
128 | 86.0k | steps_left = MAX_STEPS; |
129 | 86.0k | zend_exception_save(); |
130 | 86.0k | zval retval, args[2]; |
131 | 86.0k | zend_function *fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("opcache_invalidate")); |
132 | 86.0k | ZEND_ASSERT(fn != NULL); |
133 | | |
134 | 86.0k | ZVAL_STRING(&args[0], FILE_NAME); |
135 | 86.0k | ZVAL_TRUE(&args[1]); |
136 | 86.0k | zend_call_known_function(fn, NULL, NULL, &retval, 2, args, NULL); |
137 | 86.0k | ZEND_ASSERT(Z_TYPE(retval) == IS_TRUE); |
138 | 86.0k | zval_ptr_dtor(&args[0]); |
139 | 86.0k | zval_ptr_dtor(&retval); |
140 | 86.0k | zend_exception_restore(); |
141 | 86.0k | } fuzzer-tracing-jit.c:opcache_invalidate Line | Count | Source | 127 | 42.6k | ZEND_ATTRIBUTE_UNUSED static void opcache_invalidate(void) { | 128 | 42.6k | steps_left = MAX_STEPS; | 129 | 42.6k | zend_exception_save(); | 130 | 42.6k | zval retval, args[2]; | 131 | 42.6k | zend_function *fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("opcache_invalidate")); | 132 | 42.6k | ZEND_ASSERT(fn != NULL); | 133 | | | 134 | 42.6k | ZVAL_STRING(&args[0], FILE_NAME); | 135 | 42.6k | ZVAL_TRUE(&args[1]); | 136 | 42.6k | zend_call_known_function(fn, NULL, NULL, &retval, 2, args, NULL); | 137 | 42.6k | ZEND_ASSERT(Z_TYPE(retval) == IS_TRUE); | 138 | 42.6k | zval_ptr_dtor(&args[0]); | 139 | 42.6k | zval_ptr_dtor(&retval); | 140 | 42.6k | zend_exception_restore(); | 141 | 42.6k | } |
fuzzer-function-jit.c:opcache_invalidate Line | Count | Source | 127 | 43.3k | ZEND_ATTRIBUTE_UNUSED static void opcache_invalidate(void) { | 128 | 43.3k | steps_left = MAX_STEPS; | 129 | 43.3k | zend_exception_save(); | 130 | 43.3k | zval retval, args[2]; | 131 | 43.3k | zend_function *fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("opcache_invalidate")); | 132 | 43.3k | ZEND_ASSERT(fn != NULL); | 133 | | | 134 | 43.3k | ZVAL_STRING(&args[0], FILE_NAME); | 135 | 43.3k | ZVAL_TRUE(&args[1]); | 136 | 43.3k | zend_call_known_function(fn, NULL, NULL, &retval, 2, args, NULL); | 137 | 43.3k | ZEND_ASSERT(Z_TYPE(retval) == IS_TRUE); | 138 | 43.3k | zval_ptr_dtor(&args[0]); | 139 | 43.3k | zval_ptr_dtor(&retval); | 140 | 43.3k | zend_exception_restore(); | 141 | 43.3k | } |
Unexecuted instantiation: fuzzer-execute.c:opcache_invalidate |
142 | | |
143 | 4 | ZEND_ATTRIBUTE_UNUSED char *get_opcache_path(void) { |
144 | | /* Try relative to cwd. */ |
145 | 4 | char *p = realpath("modules/opcache.so", NULL); |
146 | 4 | if (p) { |
147 | 4 | return p; |
148 | 4 | } |
149 | | |
150 | | /* Try relative to binary location. */ |
151 | 0 | char path[MAXPATHLEN]; |
152 | | #if defined(__FreeBSD__) |
153 | | size_t pathlen = sizeof(path); |
154 | | int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; |
155 | | if (sysctl(mib, 4, path, &pathlen, NULL, 0) < 0) { |
156 | | #else |
157 | 0 | if (readlink("/proc/self/exe", path, sizeof(path)) < 0) { |
158 | 0 | #endif |
159 | 0 | ZEND_ASSERT(0 && "Failed to get binary path"); |
160 | 0 | return NULL; |
161 | 0 | } |
162 | | |
163 | | /* Get basename. */ |
164 | 0 | char *last_sep = strrchr(path, '/'); |
165 | 0 | if (last_sep) { |
166 | 0 | *last_sep = '\0'; |
167 | 0 | } |
168 | |
|
169 | 0 | strlcat(path, "/modules/opcache.so", sizeof(path)); |
170 | 0 | return realpath(path, NULL); |
171 | 0 | } |