/src/php-src/Zend/zend_string.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend Engine | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | |
6 | | +----------------------------------------------------------------------+ |
7 | | | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. | |
11 | | | If you did not receive a copy of the Zend license and are unable to | |
12 | | | obtain it through the world-wide-web, please send a note to | |
13 | | | license@zend.com so we can mail you a copy immediately. | |
14 | | +----------------------------------------------------------------------+ |
15 | | | Authors: Dmitry Stogov <dmitry@php.net> | |
16 | | +----------------------------------------------------------------------+ |
17 | | */ |
18 | | |
19 | | #include "zend.h" |
20 | | #include "zend_globals.h" |
21 | | |
22 | | #ifdef HAVE_VALGRIND |
23 | | # include "valgrind/callgrind.h" |
24 | | #endif |
25 | | |
26 | | #if __has_feature(memory_sanitizer) |
27 | | # include <sanitizer/msan_interface.h> |
28 | | #endif |
29 | | |
30 | | ZEND_API zend_new_interned_string_func_t zend_new_interned_string; |
31 | | ZEND_API zend_string_init_interned_func_t zend_string_init_interned; |
32 | | ZEND_API zend_string_init_existing_interned_func_t zend_string_init_existing_interned; |
33 | | |
34 | | static zend_string* ZEND_FASTCALL zend_new_interned_string_permanent(zend_string *str); |
35 | | static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *str); |
36 | | static zend_string* ZEND_FASTCALL zend_string_init_interned_permanent(const char *str, size_t size, bool permanent); |
37 | | static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_permanent(const char *str, size_t size, bool permanent); |
38 | | static zend_string* ZEND_FASTCALL zend_string_init_interned_request(const char *str, size_t size, bool permanent); |
39 | | static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_request(const char *str, size_t size, bool permanent); |
40 | | |
41 | | /* Any strings interned in the startup phase. Common to all the threads, |
42 | | won't be free'd until process exit. If we want an ability to |
43 | | add permanent strings even after startup, it would be still |
44 | | possible on costs of locking in the thread safe builds. */ |
45 | | static HashTable interned_strings_permanent; |
46 | | |
47 | | static zend_new_interned_string_func_t interned_string_request_handler = zend_new_interned_string_request; |
48 | | static zend_string_init_interned_func_t interned_string_init_request_handler = zend_string_init_interned_request; |
49 | | static zend_string_init_existing_interned_func_t interned_string_init_existing_request_handler = zend_string_init_existing_interned_request; |
50 | | |
51 | | ZEND_API zend_string *zend_empty_string = NULL; |
52 | | ZEND_API zend_string *zend_one_char_string[256]; |
53 | | ZEND_API zend_string **zend_known_strings = NULL; |
54 | | |
55 | | ZEND_API zend_ulong ZEND_FASTCALL zend_string_hash_func(zend_string *str) |
56 | 5.56M | { |
57 | 5.56M | return ZSTR_H(str) = zend_hash_func(ZSTR_VAL(str), ZSTR_LEN(str)); |
58 | 5.56M | } |
59 | | |
60 | | ZEND_API zend_ulong ZEND_FASTCALL zend_hash_func(const char *str, size_t len) |
61 | 5.97M | { |
62 | 5.97M | return zend_inline_hash_func(str, len); |
63 | 5.97M | } |
64 | | |
65 | | static void _str_dtor(zval *zv) |
66 | 313k | { |
67 | 313k | zend_string *str = Z_STR_P(zv); |
68 | 313k | pefree(str, GC_FLAGS(str) & IS_STR_PERSISTENT); |
69 | 313k | } |
70 | | |
71 | | static const char *known_strings[] = { |
72 | | #define _ZEND_STR_DSC(id, str) str, |
73 | | ZEND_KNOWN_STRINGS(_ZEND_STR_DSC) |
74 | | #undef _ZEND_STR_DSC |
75 | | NULL |
76 | | }; |
77 | | |
78 | | static zend_always_inline void zend_init_interned_strings_ht(HashTable *interned_strings, bool permanent) |
79 | 300k | { |
80 | 300k | zend_hash_init(interned_strings, 1024, NULL, _str_dtor, permanent); |
81 | 300k | if (permanent) { |
82 | 16 | zend_hash_real_init_mixed(interned_strings); |
83 | 16 | } |
84 | 300k | } |
85 | | |
86 | | ZEND_API void zend_interned_strings_init(void) |
87 | 16 | { |
88 | 16 | char s[2]; |
89 | 16 | unsigned int i; |
90 | 16 | zend_string *str; |
91 | | |
92 | 16 | interned_string_request_handler = zend_new_interned_string_request; |
93 | 16 | interned_string_init_request_handler = zend_string_init_interned_request; |
94 | 16 | interned_string_init_existing_request_handler = zend_string_init_existing_interned_request; |
95 | | |
96 | 16 | zend_empty_string = NULL; |
97 | 16 | zend_known_strings = NULL; |
98 | | |
99 | 16 | zend_init_interned_strings_ht(&interned_strings_permanent, 1); |
100 | | |
101 | 16 | zend_new_interned_string = zend_new_interned_string_permanent; |
102 | 16 | zend_string_init_interned = zend_string_init_interned_permanent; |
103 | 16 | zend_string_init_existing_interned = zend_string_init_existing_interned_permanent; |
104 | | |
105 | | /* interned empty string */ |
106 | 16 | str = zend_string_alloc(sizeof("")-1, 1); |
107 | 16 | ZSTR_VAL(str)[0] = '\000'; |
108 | 16 | zend_empty_string = zend_new_interned_string_permanent(str); |
109 | 16 | GC_ADD_FLAGS(zend_empty_string, IS_STR_VALID_UTF8); |
110 | | |
111 | 16 | s[1] = 0; |
112 | 4.11k | for (i = 0; i < 256; i++) { |
113 | 4.09k | s[0] = i; |
114 | 4.09k | zend_one_char_string[i] = zend_new_interned_string_permanent(zend_string_init(s, 1, 1)); |
115 | 4.09k | if (i < 0x80) { |
116 | 2.04k | GC_ADD_FLAGS(zend_one_char_string[i], IS_STR_VALID_UTF8); |
117 | 2.04k | } |
118 | 4.09k | } |
119 | | |
120 | | /* known strings */ |
121 | 16 | zend_known_strings = pemalloc(sizeof(zend_string*) * ((sizeof(known_strings) / sizeof(known_strings[0]) - 1)), 1); |
122 | 1.31k | for (i = 0; i < (sizeof(known_strings) / sizeof(known_strings[0])) - 1; i++) { |
123 | 1.29k | str = zend_string_init(known_strings[i], strlen(known_strings[i]), 1); |
124 | 1.29k | zend_known_strings[i] = zend_new_interned_string_permanent(str); |
125 | 1.29k | GC_ADD_FLAGS(zend_known_strings[i], IS_STR_VALID_UTF8); |
126 | 1.29k | } |
127 | 16 | } |
128 | | |
129 | | ZEND_API void zend_interned_strings_dtor(void) |
130 | 0 | { |
131 | 0 | zend_hash_destroy(&interned_strings_permanent); |
132 | |
|
133 | 0 | free(zend_known_strings); |
134 | 0 | zend_known_strings = NULL; |
135 | 0 | } |
136 | | |
137 | | static zend_always_inline zend_string *zend_interned_string_ht_lookup_ex(zend_ulong h, const char *str, size_t size, HashTable *interned_strings) |
138 | 2.55M | { |
139 | 2.55M | uint32_t nIndex; |
140 | 2.55M | uint32_t idx; |
141 | 2.55M | Bucket *p; |
142 | | |
143 | 2.55M | nIndex = h | interned_strings->nTableMask; |
144 | 2.55M | idx = HT_HASH(interned_strings, nIndex); |
145 | 2.98M | while (idx != HT_INVALID_IDX) { |
146 | 1.76M | p = HT_HASH_TO_BUCKET(interned_strings, idx); |
147 | 1.76M | if ((p->h == h) && zend_string_equals_cstr(p->key, str, size)) { |
148 | 1.33M | return p->key; |
149 | 1.33M | } |
150 | 427k | idx = Z_NEXT(p->val); |
151 | 427k | } |
152 | | |
153 | 1.22M | return NULL; |
154 | 2.55M | } |
155 | | |
156 | | static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_string *str, HashTable *interned_strings) |
157 | 1.82M | { |
158 | 1.82M | zend_ulong h = ZSTR_H(str); |
159 | 1.82M | uint32_t nIndex; |
160 | 1.82M | uint32_t idx; |
161 | 1.82M | Bucket *p; |
162 | | |
163 | 1.82M | nIndex = h | interned_strings->nTableMask; |
164 | 1.82M | idx = HT_HASH(interned_strings, nIndex); |
165 | 2.25M | while (idx != HT_INVALID_IDX) { |
166 | 1.25M | p = HT_HASH_TO_BUCKET(interned_strings, idx); |
167 | 1.25M | if ((p->h == h) && zend_string_equal_content(p->key, str)) { |
168 | 821k | return p->key; |
169 | 821k | } |
170 | 437k | idx = Z_NEXT(p->val); |
171 | 437k | } |
172 | | |
173 | 999k | return NULL; |
174 | 1.82M | } |
175 | | |
176 | | /* This function might be not thread safe at least because it would update the |
177 | | hash val in the passed string. Be sure it is called in the appropriate context. */ |
178 | | static zend_always_inline zend_string *zend_add_interned_string(zend_string *str, HashTable *interned_strings, uint32_t flags) |
179 | 366k | { |
180 | 366k | zval val; |
181 | | |
182 | 366k | GC_SET_REFCOUNT(str, 1); |
183 | 366k | GC_ADD_FLAGS(str, IS_STR_INTERNED | flags); |
184 | | |
185 | 366k | ZVAL_INTERNED_STR(&val, str); |
186 | | |
187 | 366k | zend_hash_add_new(interned_strings, str, &val); |
188 | | |
189 | 366k | return str; |
190 | 366k | } |
191 | | |
192 | | ZEND_API zend_string* ZEND_FASTCALL zend_interned_string_find_permanent(zend_string *str) |
193 | 0 | { |
194 | 0 | zend_string_hash_val(str); |
195 | 0 | return zend_interned_string_ht_lookup(str, &interned_strings_permanent); |
196 | 0 | } |
197 | | |
198 | | static zend_string* ZEND_FASTCALL zend_init_string_for_interning(zend_string *str, bool persistent) |
199 | 136k | { |
200 | 136k | uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(str); |
201 | 136k | zend_ulong h = ZSTR_H(str); |
202 | 136k | zend_string_delref(str); |
203 | 136k | str = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), persistent); |
204 | 136k | GC_ADD_FLAGS(str, flags); |
205 | 136k | ZSTR_H(str) = h; |
206 | 136k | return str; |
207 | 136k | } |
208 | | |
209 | | static zend_string* ZEND_FASTCALL zend_new_interned_string_permanent(zend_string *str) |
210 | 40.3k | { |
211 | 40.3k | zend_string *ret; |
212 | | |
213 | 40.3k | if (ZSTR_IS_INTERNED(str)) { |
214 | 22.2k | return str; |
215 | 22.2k | } |
216 | | |
217 | 18.1k | zend_string_hash_val(str); |
218 | 18.1k | ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent); |
219 | 18.1k | if (ret) { |
220 | 4.07k | zend_string_release(str); |
221 | 4.07k | return ret; |
222 | 4.07k | } |
223 | | |
224 | 14.0k | ZEND_ASSERT(GC_FLAGS(str) & GC_PERSISTENT); |
225 | 14.0k | if (GC_REFCOUNT(str) > 1) { |
226 | 338 | str = zend_init_string_for_interning(str, true); |
227 | 338 | } |
228 | | |
229 | 14.0k | return zend_add_interned_string(str, &interned_strings_permanent, IS_STR_PERMANENT); |
230 | 14.0k | } |
231 | | |
232 | | static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *str) |
233 | 1.57M | { |
234 | 1.57M | zend_string *ret; |
235 | | |
236 | 1.57M | if (ZSTR_IS_INTERNED(str)) { |
237 | 522k | return str; |
238 | 522k | } |
239 | | |
240 | 1.04M | zend_string_hash_val(str); |
241 | | |
242 | | /* Check for permanent strings, the table is readonly at this point. */ |
243 | 1.04M | ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent); |
244 | 1.04M | if (ret) { |
245 | 292k | zend_string_release(str); |
246 | 292k | return ret; |
247 | 292k | } |
248 | | |
249 | 755k | ret = zend_interned_string_ht_lookup(str, &CG(interned_strings)); |
250 | 755k | if (ret) { |
251 | 525k | zend_string_release(str); |
252 | 525k | return ret; |
253 | 525k | } |
254 | | |
255 | | /* Create a short living interned, freed after the request. */ |
256 | | #if ZEND_RC_DEBUG |
257 | | if (zend_rc_debug) { |
258 | | /* PHP shouldn't create persistent interned string during request, |
259 | | * but at least dl() may do this */ |
260 | | ZEND_ASSERT(!(GC_FLAGS(str) & GC_PERSISTENT)); |
261 | | } |
262 | | #endif |
263 | 229k | if (GC_REFCOUNT(str) > 1) { |
264 | 136k | str = zend_init_string_for_interning(str, false); |
265 | 136k | } |
266 | | |
267 | 229k | ret = zend_add_interned_string(str, &CG(interned_strings), 0); |
268 | | |
269 | 229k | return ret; |
270 | 755k | } |
271 | | |
272 | | static zend_string* ZEND_FASTCALL zend_string_init_interned_permanent(const char *str, size_t size, bool permanent) |
273 | 58.3k | { |
274 | 58.3k | zend_string *ret; |
275 | 58.3k | zend_ulong h = zend_inline_hash_func(str, size); |
276 | | |
277 | 58.3k | ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); |
278 | 58.3k | if (ret) { |
279 | 18.8k | return ret; |
280 | 18.8k | } |
281 | | |
282 | 39.4k | ZEND_ASSERT(permanent); |
283 | 39.4k | ret = zend_string_init(str, size, permanent); |
284 | 39.4k | ZSTR_H(ret) = h; |
285 | 39.4k | return zend_add_interned_string(ret, &interned_strings_permanent, IS_STR_PERMANENT); |
286 | 39.4k | } |
287 | | |
288 | | static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_permanent(const char *str, size_t size, bool permanent) |
289 | 0 | { |
290 | 0 | zend_ulong h = zend_inline_hash_func(str, size); |
291 | 0 | zend_string *ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); |
292 | 0 | if (ret) { |
293 | 0 | return ret; |
294 | 0 | } |
295 | | |
296 | 0 | ZEND_ASSERT(permanent); |
297 | 0 | ret = zend_string_init(str, size, permanent); |
298 | 0 | ZSTR_H(ret) = h; |
299 | 0 | return ret; |
300 | 0 | } |
301 | | |
302 | | static zend_string* ZEND_FASTCALL zend_string_init_interned_request(const char *str, size_t size, bool permanent) |
303 | 1.11M | { |
304 | 1.11M | zend_string *ret; |
305 | 1.11M | zend_ulong h = zend_inline_hash_func(str, size); |
306 | | |
307 | | /* Check for permanent strings, the table is readonly at this point. */ |
308 | 1.11M | ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); |
309 | 1.11M | if (ret) { |
310 | 715k | return ret; |
311 | 715k | } |
312 | | |
313 | 403k | ret = zend_interned_string_ht_lookup_ex(h, str, size, &CG(interned_strings)); |
314 | 403k | if (ret) { |
315 | 320k | return ret; |
316 | 320k | } |
317 | | |
318 | | #if ZEND_RC_DEBUG |
319 | | if (zend_rc_debug) { |
320 | | /* PHP shouldn't create persistent interned string during request, |
321 | | * but at least dl() may do this */ |
322 | | ZEND_ASSERT(!permanent); |
323 | | } |
324 | | #endif |
325 | 83.4k | ret = zend_string_init(str, size, permanent); |
326 | 83.4k | ZSTR_H(ret) = h; |
327 | | |
328 | | /* Create a short living interned, freed after the request. */ |
329 | 83.4k | return zend_add_interned_string(ret, &CG(interned_strings), 0); |
330 | 403k | } |
331 | | |
332 | | static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_request(const char *str, size_t size, bool permanent) |
333 | 614k | { |
334 | 614k | zend_ulong h = zend_inline_hash_func(str, size); |
335 | 614k | zend_string *ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); |
336 | 614k | if (ret) { |
337 | 250k | return ret; |
338 | 250k | } |
339 | | |
340 | 363k | ret = zend_interned_string_ht_lookup_ex(h, str, size, &CG(interned_strings)); |
341 | 363k | if (ret) { |
342 | 28.3k | return ret; |
343 | 28.3k | } |
344 | | |
345 | 335k | ZEND_ASSERT(!permanent); |
346 | 335k | ret = zend_string_init(str, size, permanent); |
347 | 335k | ZSTR_H(ret) = h; |
348 | 335k | return ret; |
349 | 335k | } |
350 | | |
351 | | ZEND_API void zend_interned_strings_activate(void) |
352 | 300k | { |
353 | 300k | zend_init_interned_strings_ht(&CG(interned_strings), 0); |
354 | 300k | } |
355 | | |
356 | | ZEND_API void zend_interned_strings_deactivate(void) |
357 | 300k | { |
358 | 300k | zend_hash_destroy(&CG(interned_strings)); |
359 | 300k | } |
360 | | |
361 | | ZEND_API void zend_interned_strings_set_request_storage_handlers(zend_new_interned_string_func_t handler, zend_string_init_interned_func_t init_handler, zend_string_init_existing_interned_func_t init_existing_handler) |
362 | 4 | { |
363 | 4 | interned_string_request_handler = handler; |
364 | 4 | interned_string_init_request_handler = init_handler; |
365 | 4 | interned_string_init_existing_request_handler = init_existing_handler; |
366 | 4 | } |
367 | | |
368 | | ZEND_API void zend_interned_strings_switch_storage(bool request) |
369 | 16 | { |
370 | 16 | if (request) { |
371 | 16 | zend_new_interned_string = interned_string_request_handler; |
372 | 16 | zend_string_init_interned = interned_string_init_request_handler; |
373 | 16 | zend_string_init_existing_interned = interned_string_init_existing_request_handler; |
374 | 16 | } else { |
375 | 0 | zend_new_interned_string = zend_new_interned_string_permanent; |
376 | 0 | zend_string_init_interned = zend_string_init_interned_permanent; |
377 | 0 | zend_string_init_existing_interned = zend_string_init_existing_interned_permanent; |
378 | 0 | } |
379 | 16 | } |
380 | | |
381 | | #if defined(__GNUC__) && (defined(__i386__) || (defined(__x86_64__) && !defined(__ILP32__))) |
382 | | /* Even if we don't build with valgrind support, include the symbol so that valgrind available |
383 | | * only at runtime will not result in false positives. */ |
384 | | #ifndef I_REPLACE_SONAME_FNNAME_ZU |
385 | | # define I_REPLACE_SONAME_FNNAME_ZU(soname, fnname) _vgr00000ZU_ ## soname ## _ ## fnname |
386 | | #endif |
387 | | |
388 | | /* See GH-9068 */ |
389 | | #if __has_attribute(noipa) |
390 | | # define NOIPA __attribute__((noipa)) |
391 | | #else |
392 | | # define NOIPA |
393 | | #endif |
394 | | |
395 | | ZEND_API bool ZEND_FASTCALL I_REPLACE_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(const zend_string *s1, const zend_string *s2) |
396 | 0 | { |
397 | 0 | return !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)); |
398 | 0 | } |
399 | | #endif |
400 | | |
401 | | #if defined(__GNUC__) && defined(__i386__) |
402 | | ZEND_API zend_never_inline NOIPA bool ZEND_FASTCALL zend_string_equal_val(const zend_string *s1, const zend_string *s2) |
403 | | { |
404 | | const char *ptr = ZSTR_VAL(s1); |
405 | | uintptr_t delta = (uintptr_t) s2 - (uintptr_t) s1; |
406 | | size_t len = ZSTR_LEN(s1); |
407 | | zend_ulong ret; |
408 | | |
409 | | __asm__ ( |
410 | | "0:\n\t" |
411 | | "movl (%2,%3), %0\n\t" |
412 | | "xorl (%2), %0\n\t" |
413 | | "jne 1f\n\t" |
414 | | "addl $0x4, %2\n\t" |
415 | | "subl $0x4, %1\n\t" |
416 | | "ja 0b\n\t" |
417 | | "movl $0x1, %0\n\t" |
418 | | "jmp 3f\n\t" |
419 | | "1:\n\t" |
420 | | "cmpl $0x4,%1\n\t" |
421 | | "jb 2f\n\t" |
422 | | "xorl %0, %0\n\t" |
423 | | "jmp 3f\n\t" |
424 | | "2:\n\t" |
425 | | "negl %1\n\t" |
426 | | "lea 0x20(,%1,8), %1\n\t" |
427 | | "shll %b1, %0\n\t" |
428 | | "sete %b0\n\t" |
429 | | "movzbl %b0, %0\n\t" |
430 | | "3:\n" |
431 | | : "=&a"(ret), |
432 | | "+c"(len), |
433 | | "+r"(ptr) |
434 | | : "r"(delta) |
435 | | : "cc"); |
436 | | return ret; |
437 | | } |
438 | | |
439 | | #elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__) |
440 | | ZEND_API zend_never_inline NOIPA bool ZEND_FASTCALL zend_string_equal_val(const zend_string *s1, const zend_string *s2) |
441 | 5.27M | { |
442 | 5.27M | const char *ptr = ZSTR_VAL(s1); |
443 | 5.27M | uintptr_t delta = (uintptr_t) s2 - (uintptr_t) s1; |
444 | 5.27M | size_t len = ZSTR_LEN(s1); |
445 | 5.27M | zend_ulong ret; |
446 | | |
447 | 5.27M | __asm__ ( |
448 | 5.27M | "0:\n\t" |
449 | 5.27M | "movq (%2,%3), %0\n\t" |
450 | 5.27M | "xorq (%2), %0\n\t" |
451 | 5.27M | "jne 1f\n\t" |
452 | 5.27M | "addq $0x8, %2\n\t" |
453 | 5.27M | "subq $0x8, %1\n\t" |
454 | 5.27M | "ja 0b\n\t" |
455 | 5.27M | "movq $0x1, %0\n\t" |
456 | 5.27M | "jmp 3f\n\t" |
457 | 5.27M | "1:\n\t" |
458 | 5.27M | "cmpq $0x8,%1\n\t" |
459 | 5.27M | "jb 2f\n\t" |
460 | 5.27M | "xorq %0, %0\n\t" |
461 | 5.27M | "jmp 3f\n\t" |
462 | 5.27M | "2:\n\t" |
463 | 5.27M | "negq %1\n\t" |
464 | 5.27M | "lea 0x40(,%1,8), %1\n\t" |
465 | 5.27M | "shlq %b1, %0\n\t" |
466 | 5.27M | "sete %b0\n\t" |
467 | 5.27M | "movzbq %b0, %0\n\t" |
468 | 5.27M | "3:\n" |
469 | 5.27M | : "=&a"(ret), |
470 | 5.27M | "+c"(len), |
471 | 5.27M | "+r"(ptr) |
472 | 5.27M | : "r"(delta) |
473 | 5.27M | : "cc"); |
474 | 5.27M | return ret; |
475 | 5.27M | } |
476 | | #endif |
477 | | |
478 | | ZEND_API zend_string *zend_string_concat2( |
479 | | const char *str1, size_t str1_len, |
480 | | const char *str2, size_t str2_len) |
481 | 216k | { |
482 | 216k | size_t len = str1_len + str2_len; |
483 | 216k | zend_string *res = zend_string_alloc(len, 0); |
484 | | |
485 | 216k | memcpy(ZSTR_VAL(res), str1, str1_len); |
486 | 216k | memcpy(ZSTR_VAL(res) + str1_len, str2, str2_len); |
487 | 216k | ZSTR_VAL(res)[len] = '\0'; |
488 | | |
489 | 216k | return res; |
490 | 216k | } |
491 | | |
492 | | ZEND_API zend_string *zend_string_concat3( |
493 | | const char *str1, size_t str1_len, |
494 | | const char *str2, size_t str2_len, |
495 | | const char *str3, size_t str3_len) |
496 | 215k | { |
497 | 215k | size_t len = str1_len + str2_len + str3_len; |
498 | 215k | zend_string *res = zend_string_alloc(len, 0); |
499 | | |
500 | 215k | memcpy(ZSTR_VAL(res), str1, str1_len); |
501 | 215k | memcpy(ZSTR_VAL(res) + str1_len, str2, str2_len); |
502 | 215k | memcpy(ZSTR_VAL(res) + str1_len + str2_len, str3, str3_len); |
503 | 215k | ZSTR_VAL(res)[len] = '\0'; |
504 | | |
505 | 215k | return res; |
506 | 215k | } |
507 | | |
508 | | /* strlcpy and strlcat are not intercepted by msan, so we need to do it ourselves. */ |
509 | | #if __has_feature(memory_sanitizer) |
510 | | static size_t (*libc_strlcpy)(char *__restrict, const char *__restrict, size_t); |
511 | | size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t n) |
512 | | { |
513 | | if (!libc_strlcpy) { |
514 | | libc_strlcpy = dlsym(RTLD_NEXT, "strlcpy"); |
515 | | } |
516 | | size_t result = libc_strlcpy(dest, src, n); |
517 | | __msan_unpoison_string(dest); |
518 | | return result; |
519 | | } |
520 | | static size_t (*libc_strlcat)(char *__restrict, const char *__restrict, size_t); |
521 | | size_t strlcat (char *__restrict dest, const char *restrict src, size_t n) |
522 | | { |
523 | | if (!libc_strlcat) { |
524 | | libc_strlcat = dlsym(RTLD_NEXT, "strlcat"); |
525 | | } |
526 | | size_t result = libc_strlcat(dest, src, n); |
527 | | __msan_unpoison_string(dest); |
528 | | return result; |
529 | | } |
530 | | #endif |