/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 | 7.85M | { |
57 | 7.85M | return ZSTR_H(str) = zend_hash_func(ZSTR_VAL(str), ZSTR_LEN(str)); |
58 | 7.85M | } |
59 | | |
60 | | ZEND_API zend_ulong ZEND_FASTCALL zend_hash_func(const char *str, size_t len) |
61 | 8.24M | { |
62 | 8.24M | return zend_inline_hash_func(str, len); |
63 | 8.24M | } |
64 | | |
65 | | static void _str_dtor(zval *zv) |
66 | 347k | { |
67 | 347k | zend_string *str = Z_STR_P(zv); |
68 | 347k | pefree(str, GC_FLAGS(str) & IS_STR_PERSISTENT); |
69 | 347k | } |
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 | 268k | { |
80 | 268k | zend_hash_init(interned_strings, 1024, NULL, _str_dtor, permanent); |
81 | 268k | if (permanent) { |
82 | 16 | zend_hash_real_init_mixed(interned_strings); |
83 | 16 | } |
84 | 268k | } |
85 | | |
86 | | ZEND_API void zend_interned_strings_init(void) |
87 | 16 | { |
88 | 16 | char s[2]; |
89 | | |
90 | 16 | interned_string_request_handler = zend_new_interned_string_request; |
91 | 16 | interned_string_init_request_handler = zend_string_init_interned_request; |
92 | 16 | interned_string_init_existing_request_handler = zend_string_init_existing_interned_request; |
93 | | |
94 | 16 | zend_empty_string = NULL; |
95 | 16 | zend_known_strings = NULL; |
96 | | |
97 | 16 | zend_init_interned_strings_ht(&interned_strings_permanent, 1); |
98 | | |
99 | 16 | zend_new_interned_string = zend_new_interned_string_permanent; |
100 | 16 | zend_string_init_interned = zend_string_init_interned_permanent; |
101 | 16 | zend_string_init_existing_interned = zend_string_init_existing_interned_permanent; |
102 | | |
103 | | /* interned empty string */ |
104 | 16 | zend_empty_string = zend_string_init_interned_permanent("", 0, true); |
105 | 16 | GC_ADD_FLAGS(zend_empty_string, IS_STR_VALID_UTF8); |
106 | | |
107 | 16 | s[1] = 0; |
108 | 4.11k | for (size_t i = 0; i < 256; i++) { |
109 | 4.09k | s[0] = i; |
110 | 4.09k | zend_one_char_string[i] = zend_string_init_interned_permanent(s, 1, true); |
111 | 4.09k | if (i < 0x80) { |
112 | 2.04k | GC_ADD_FLAGS(zend_one_char_string[i], IS_STR_VALID_UTF8); |
113 | 2.04k | } |
114 | 4.09k | } |
115 | | |
116 | | /* known strings */ |
117 | 16 | zend_known_strings = pemalloc(sizeof(zend_string*) * ((sizeof(known_strings) / sizeof(known_strings[0]) - 1)), 1); |
118 | 1.42k | for (size_t i = 0; i < (sizeof(known_strings) / sizeof(known_strings[0])) - 1; i++) { |
119 | 1.40k | zend_known_strings[i] = zend_string_init_interned_permanent(known_strings[i], strlen(known_strings[i]), true); |
120 | 1.40k | GC_ADD_FLAGS(zend_known_strings[i], IS_STR_VALID_UTF8); |
121 | 1.40k | } |
122 | 16 | } |
123 | | |
124 | | ZEND_API void zend_interned_strings_dtor(void) |
125 | 0 | { |
126 | 0 | zend_hash_destroy(&interned_strings_permanent); |
127 | |
|
128 | 0 | free(zend_known_strings); |
129 | 0 | zend_known_strings = NULL; |
130 | 0 | } |
131 | | |
132 | | static zend_always_inline zend_string *zend_interned_string_ht_lookup_ex(zend_ulong h, const char *str, size_t size, HashTable *interned_strings) |
133 | 2.07M | { |
134 | 2.07M | uint32_t nIndex; |
135 | 2.07M | uint32_t idx; |
136 | 2.07M | Bucket *p; |
137 | | |
138 | 2.07M | nIndex = h | interned_strings->nTableMask; |
139 | 2.07M | idx = HT_HASH(interned_strings, nIndex); |
140 | 2.42M | while (idx != HT_INVALID_IDX) { |
141 | 1.41M | p = HT_HASH_TO_BUCKET(interned_strings, idx); |
142 | 1.41M | if ((p->h == h) && zend_string_equals_cstr(p->key, str, size)) { |
143 | 1.06M | return p->key; |
144 | 1.06M | } |
145 | 347k | idx = Z_NEXT(p->val); |
146 | 347k | } |
147 | | |
148 | 1.01M | return NULL; |
149 | 2.07M | } |
150 | | |
151 | | static zend_always_inline zend_string *zend_interned_string_ht_lookup(zend_string *str, HashTable *interned_strings) |
152 | 5.56M | { |
153 | 5.56M | zend_ulong h = ZSTR_H(str); |
154 | 5.56M | uint32_t nIndex; |
155 | 5.56M | uint32_t idx; |
156 | 5.56M | Bucket *p; |
157 | | |
158 | 5.56M | nIndex = h | interned_strings->nTableMask; |
159 | 5.56M | idx = HT_HASH(interned_strings, nIndex); |
160 | 7.04M | while (idx != HT_INVALID_IDX) { |
161 | 4.17M | p = HT_HASH_TO_BUCKET(interned_strings, idx); |
162 | 4.17M | if ((p->h == h) && zend_string_equal_content(p->key, str)) { |
163 | 2.69M | return p->key; |
164 | 2.69M | } |
165 | 1.48M | idx = Z_NEXT(p->val); |
166 | 1.48M | } |
167 | | |
168 | 2.87M | return NULL; |
169 | 5.56M | } |
170 | | |
171 | | /* This function might be not thread safe at least because it would update the |
172 | | hash val in the passed string. Be sure it is called in the appropriate context. */ |
173 | | static zend_always_inline zend_string *zend_add_interned_string(zend_string *str, HashTable *interned_strings, uint32_t flags) |
174 | 401k | { |
175 | 401k | zval val; |
176 | | |
177 | 401k | GC_SET_REFCOUNT(str, 1); |
178 | 401k | GC_ADD_FLAGS(str, IS_STR_INTERNED | flags); |
179 | | |
180 | 401k | ZVAL_INTERNED_STR(&val, str); |
181 | | |
182 | 401k | zend_hash_add_new(interned_strings, str, &val); |
183 | | |
184 | 401k | return str; |
185 | 401k | } |
186 | | |
187 | | ZEND_API zend_string* ZEND_FASTCALL zend_interned_string_find_permanent(zend_string *str) |
188 | 0 | { |
189 | 0 | zend_string_hash_val(str); |
190 | 0 | return zend_interned_string_ht_lookup(str, &interned_strings_permanent); |
191 | 0 | } |
192 | | |
193 | | static zend_string* ZEND_FASTCALL zend_init_string_for_interning(zend_string *str, bool persistent) |
194 | 147k | { |
195 | 147k | uint32_t flags = ZSTR_GET_COPYABLE_CONCAT_PROPERTIES(str); |
196 | 147k | zend_ulong h = ZSTR_H(str); |
197 | 147k | zend_string_delref(str); |
198 | 147k | str = zend_string_init(ZSTR_VAL(str), ZSTR_LEN(str), persistent); |
199 | 147k | GC_ADD_FLAGS(str, flags); |
200 | 147k | ZSTR_H(str) = h; |
201 | 147k | return str; |
202 | 147k | } |
203 | | |
204 | | static zend_string* ZEND_FASTCALL zend_new_interned_string_permanent(zend_string *str) |
205 | 35.4k | { |
206 | 35.4k | zend_string *ret; |
207 | | |
208 | 35.4k | if (ZSTR_IS_INTERNED(str)) { |
209 | 22.4k | return str; |
210 | 22.4k | } |
211 | | |
212 | 13.0k | zend_string_hash_val(str); |
213 | 13.0k | ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent); |
214 | 13.0k | if (ret) { |
215 | 4.22k | zend_string_release(str); |
216 | 4.22k | return ret; |
217 | 4.22k | } |
218 | | |
219 | 8.85k | ZEND_ASSERT(GC_FLAGS(str) & GC_PERSISTENT); |
220 | 8.85k | if (GC_REFCOUNT(str) > 1) { |
221 | 338 | str = zend_init_string_for_interning(str, true); |
222 | 338 | } |
223 | | |
224 | 8.85k | return zend_add_interned_string(str, &interned_strings_permanent, IS_STR_PERMANENT); |
225 | 8.85k | } |
226 | | |
227 | | static zend_string* ZEND_FASTCALL zend_new_interned_string_request(zend_string *str) |
228 | 3.71M | { |
229 | 3.71M | zend_string *ret; |
230 | | |
231 | 3.71M | if (ZSTR_IS_INTERNED(str)) { |
232 | 745k | return str; |
233 | 745k | } |
234 | | |
235 | 2.96M | zend_string_hash_val(str); |
236 | | |
237 | | /* Check for permanent strings, the table is readonly at this point. */ |
238 | 2.96M | ret = zend_interned_string_ht_lookup(str, &interned_strings_permanent); |
239 | 2.96M | if (ret) { |
240 | 385k | zend_string_release(str); |
241 | 385k | return ret; |
242 | 385k | } |
243 | | |
244 | 2.58M | ret = zend_interned_string_ht_lookup(str, &CG(interned_strings)); |
245 | 2.58M | if (ret) { |
246 | 2.30M | zend_string_release(str); |
247 | 2.30M | return ret; |
248 | 2.30M | } |
249 | | |
250 | | /* Create a short living interned, freed after the request. */ |
251 | | #if ZEND_RC_DEBUG |
252 | | if (zend_rc_debug) { |
253 | | /* PHP shouldn't create persistent interned string during request, |
254 | | * but at least dl() may do this */ |
255 | | ZEND_ASSERT(!(GC_FLAGS(str) & GC_PERSISTENT)); |
256 | | } |
257 | | #endif |
258 | 280k | if (GC_REFCOUNT(str) > 1) { |
259 | 147k | str = zend_init_string_for_interning(str, false); |
260 | 147k | } |
261 | | |
262 | 280k | ret = zend_add_interned_string(str, &CG(interned_strings), 0); |
263 | | |
264 | 280k | return ret; |
265 | 2.58M | } |
266 | | |
267 | | static zend_string* ZEND_FASTCALL zend_string_init_interned_permanent(const char *str, size_t size, bool permanent) |
268 | 64.3k | { |
269 | 64.3k | zend_string *ret; |
270 | 64.3k | zend_ulong h = zend_inline_hash_func(str, size); |
271 | | |
272 | 64.3k | ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); |
273 | 64.3k | if (ret) { |
274 | 19.1k | return ret; |
275 | 19.1k | } |
276 | | |
277 | 45.2k | ZEND_ASSERT(permanent); |
278 | 45.2k | ret = zend_string_init(str, size, permanent); |
279 | 45.2k | ZSTR_H(ret) = h; |
280 | 45.2k | return zend_add_interned_string(ret, &interned_strings_permanent, IS_STR_PERMANENT); |
281 | 45.2k | } |
282 | | |
283 | | static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_permanent(const char *str, size_t size, bool permanent) |
284 | 0 | { |
285 | 0 | zend_ulong h = zend_inline_hash_func(str, size); |
286 | 0 | zend_string *ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); |
287 | 0 | if (ret) { |
288 | 0 | return ret; |
289 | 0 | } |
290 | | |
291 | 0 | ZEND_ASSERT(permanent); |
292 | 0 | ret = zend_string_init(str, size, permanent); |
293 | 0 | ZSTR_H(ret) = h; |
294 | 0 | return ret; |
295 | 0 | } |
296 | | |
297 | | static zend_string* ZEND_FASTCALL zend_string_init_interned_request(const char *str, size_t size, bool permanent) |
298 | 880k | { |
299 | 880k | zend_string *ret; |
300 | 880k | zend_ulong h = zend_inline_hash_func(str, size); |
301 | | |
302 | | /* Check for permanent strings, the table is readonly at this point. */ |
303 | 880k | ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); |
304 | 880k | if (ret) { |
305 | 511k | return ret; |
306 | 511k | } |
307 | | |
308 | 368k | ret = zend_interned_string_ht_lookup_ex(h, str, size, &CG(interned_strings)); |
309 | 368k | if (ret) { |
310 | 302k | return ret; |
311 | 302k | } |
312 | | |
313 | | #if ZEND_RC_DEBUG |
314 | | if (zend_rc_debug) { |
315 | | /* PHP shouldn't create persistent interned string during request, |
316 | | * but at least dl() may do this */ |
317 | | ZEND_ASSERT(!permanent); |
318 | | } |
319 | | #endif |
320 | 66.6k | ret = zend_string_init(str, size, permanent); |
321 | 66.6k | ZSTR_H(ret) = h; |
322 | | |
323 | | /* Create a short living interned, freed after the request. */ |
324 | 66.6k | return zend_add_interned_string(ret, &CG(interned_strings), 0); |
325 | 368k | } |
326 | | |
327 | | static zend_string* ZEND_FASTCALL zend_string_init_existing_interned_request(const char *str, size_t size, bool permanent) |
328 | 489k | { |
329 | 489k | zend_ulong h = zend_inline_hash_func(str, size); |
330 | 489k | zend_string *ret = zend_interned_string_ht_lookup_ex(h, str, size, &interned_strings_permanent); |
331 | 489k | if (ret) { |
332 | 214k | return ret; |
333 | 214k | } |
334 | | |
335 | 274k | ret = zend_interned_string_ht_lookup_ex(h, str, size, &CG(interned_strings)); |
336 | 274k | if (ret) { |
337 | 15.5k | return ret; |
338 | 15.5k | } |
339 | | |
340 | 259k | ZEND_ASSERT(!permanent); |
341 | 259k | ret = zend_string_init(str, size, permanent); |
342 | 259k | ZSTR_H(ret) = h; |
343 | 259k | return ret; |
344 | 259k | } |
345 | | |
346 | | ZEND_API void zend_interned_strings_activate(void) |
347 | 268k | { |
348 | 268k | zend_init_interned_strings_ht(&CG(interned_strings), 0); |
349 | 268k | } |
350 | | |
351 | | ZEND_API void zend_interned_strings_deactivate(void) |
352 | 268k | { |
353 | 268k | zend_hash_destroy(&CG(interned_strings)); |
354 | 268k | } |
355 | | |
356 | | 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) |
357 | 4 | { |
358 | 4 | interned_string_request_handler = handler; |
359 | 4 | interned_string_init_request_handler = init_handler; |
360 | 4 | interned_string_init_existing_request_handler = init_existing_handler; |
361 | 4 | } |
362 | | |
363 | | ZEND_API void zend_interned_strings_switch_storage(bool request) |
364 | 16 | { |
365 | 16 | if (request) { |
366 | 16 | zend_new_interned_string = interned_string_request_handler; |
367 | 16 | zend_string_init_interned = interned_string_init_request_handler; |
368 | 16 | zend_string_init_existing_interned = interned_string_init_existing_request_handler; |
369 | 16 | } else { |
370 | 0 | zend_new_interned_string = zend_new_interned_string_permanent; |
371 | 0 | zend_string_init_interned = zend_string_init_interned_permanent; |
372 | 0 | zend_string_init_existing_interned = zend_string_init_existing_interned_permanent; |
373 | 0 | } |
374 | 16 | } |
375 | | |
376 | | #if defined(__GNUC__) && (defined(__i386__) || (defined(__x86_64__) && !defined(__ILP32__))) |
377 | | /* Even if we don't build with valgrind support, include the symbol so that valgrind available |
378 | | * only at runtime will not result in false positives. */ |
379 | | #ifndef I_REPLACE_SONAME_FNNAME_ZU |
380 | | # define I_REPLACE_SONAME_FNNAME_ZU(soname, fnname) _vgr00000ZU_ ## soname ## _ ## fnname |
381 | | #endif |
382 | | |
383 | | /* See GH-9068 */ |
384 | | #if __has_attribute(noipa) |
385 | | # define NOIPA __attribute__((noipa)) |
386 | | #else |
387 | | # define NOIPA |
388 | | #endif |
389 | | |
390 | | ZEND_API bool ZEND_FASTCALL I_REPLACE_SONAME_FNNAME_ZU(NONE,zend_string_equal_val)(const zend_string *s1, const zend_string *s2) |
391 | 0 | { |
392 | 0 | return !memcmp(ZSTR_VAL(s1), ZSTR_VAL(s2), ZSTR_LEN(s1)); |
393 | 0 | } |
394 | | #endif |
395 | | |
396 | | #if defined(__GNUC__) && defined(__i386__) |
397 | | ZEND_API zend_never_inline NOIPA bool ZEND_FASTCALL zend_string_equal_val(const zend_string *s1, const zend_string *s2) |
398 | | { |
399 | | const char *ptr = ZSTR_VAL(s1); |
400 | | uintptr_t delta = (uintptr_t) s2 - (uintptr_t) s1; |
401 | | size_t len = ZSTR_LEN(s1); |
402 | | zend_ulong ret; |
403 | | |
404 | | __asm__ ( |
405 | | "0:\n\t" |
406 | | "movl (%2,%3), %0\n\t" |
407 | | "xorl (%2), %0\n\t" |
408 | | "jne 1f\n\t" |
409 | | "addl $0x4, %2\n\t" |
410 | | "subl $0x4, %1\n\t" |
411 | | "ja 0b\n\t" |
412 | | "movl $0x1, %0\n\t" |
413 | | "jmp 3f\n\t" |
414 | | "1:\n\t" |
415 | | "cmpl $0x4,%1\n\t" |
416 | | "jb 2f\n\t" |
417 | | "xorl %0, %0\n\t" |
418 | | "jmp 3f\n\t" |
419 | | "2:\n\t" |
420 | | "negl %1\n\t" |
421 | | "lea 0x20(,%1,8), %1\n\t" |
422 | | "shll %b1, %0\n\t" |
423 | | "sete %b0\n\t" |
424 | | "movzbl %b0, %0\n\t" |
425 | | "3:\n" |
426 | | : "=&a"(ret), |
427 | | "+c"(len), |
428 | | "+r"(ptr) |
429 | | : "r"(delta) |
430 | | : "cc"); |
431 | | return ret; |
432 | | } |
433 | | |
434 | | #elif defined(__GNUC__) && defined(__x86_64__) && !defined(__ILP32__) |
435 | | ZEND_API zend_never_inline NOIPA bool ZEND_FASTCALL zend_string_equal_val(const zend_string *s1, const zend_string *s2) |
436 | 7.02M | { |
437 | 7.02M | const char *ptr = ZSTR_VAL(s1); |
438 | 7.02M | uintptr_t delta = (uintptr_t) s2 - (uintptr_t) s1; |
439 | 7.02M | size_t len = ZSTR_LEN(s1); |
440 | 7.02M | zend_ulong ret; |
441 | | |
442 | 7.02M | __asm__ ( |
443 | 7.02M | "0:\n\t" |
444 | 7.02M | "movq (%2,%3), %0\n\t" |
445 | 7.02M | "xorq (%2), %0\n\t" |
446 | 7.02M | "jne 1f\n\t" |
447 | 7.02M | "addq $0x8, %2\n\t" |
448 | 7.02M | "subq $0x8, %1\n\t" |
449 | 7.02M | "ja 0b\n\t" |
450 | 7.02M | "movq $0x1, %0\n\t" |
451 | 7.02M | "jmp 3f\n\t" |
452 | 7.02M | "1:\n\t" |
453 | 7.02M | "cmpq $0x8,%1\n\t" |
454 | 7.02M | "jb 2f\n\t" |
455 | 7.02M | "xorq %0, %0\n\t" |
456 | 7.02M | "jmp 3f\n\t" |
457 | 7.02M | "2:\n\t" |
458 | 7.02M | "negq %1\n\t" |
459 | 7.02M | "lea 0x40(,%1,8), %1\n\t" |
460 | 7.02M | "shlq %b1, %0\n\t" |
461 | 7.02M | "sete %b0\n\t" |
462 | 7.02M | "movzbq %b0, %0\n\t" |
463 | 7.02M | "3:\n" |
464 | 7.02M | : "=&a"(ret), |
465 | 7.02M | "+c"(len), |
466 | 7.02M | "+r"(ptr) |
467 | 7.02M | : "r"(delta) |
468 | 7.02M | : "cc"); |
469 | 7.02M | return ret; |
470 | 7.02M | } |
471 | | #endif |
472 | | |
473 | | ZEND_API zend_string *zend_string_concat2( |
474 | | const char *str1, size_t str1_len, |
475 | | const char *str2, size_t str2_len) |
476 | 263k | { |
477 | 263k | size_t len = str1_len + str2_len; |
478 | 263k | zend_string *res = zend_string_alloc(len, 0); |
479 | | |
480 | 263k | memcpy(ZSTR_VAL(res), str1, str1_len); |
481 | 263k | memcpy(ZSTR_VAL(res) + str1_len, str2, str2_len); |
482 | 263k | ZSTR_VAL(res)[len] = '\0'; |
483 | | |
484 | 263k | return res; |
485 | 263k | } |
486 | | |
487 | | ZEND_API zend_string *zend_string_concat3( |
488 | | const char *str1, size_t str1_len, |
489 | | const char *str2, size_t str2_len, |
490 | | const char *str3, size_t str3_len) |
491 | 1.27M | { |
492 | 1.27M | size_t len = str1_len + str2_len + str3_len; |
493 | 1.27M | zend_string *res = zend_string_alloc(len, 0); |
494 | | |
495 | 1.27M | memcpy(ZSTR_VAL(res), str1, str1_len); |
496 | 1.27M | memcpy(ZSTR_VAL(res) + str1_len, str2, str2_len); |
497 | 1.27M | memcpy(ZSTR_VAL(res) + str1_len + str2_len, str3, str3_len); |
498 | 1.27M | ZSTR_VAL(res)[len] = '\0'; |
499 | | |
500 | 1.27M | return res; |
501 | 1.27M | } |
502 | | |
503 | | /* strlcpy and strlcat are not intercepted by msan, so we need to do it ourselves. */ |
504 | | #if __has_feature(memory_sanitizer) |
505 | | static size_t (*libc_strlcpy)(char *__restrict, const char *__restrict, size_t); |
506 | | size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t n) |
507 | | { |
508 | | if (!libc_strlcpy) { |
509 | | libc_strlcpy = dlsym(RTLD_NEXT, "strlcpy"); |
510 | | } |
511 | | size_t result = libc_strlcpy(dest, src, n); |
512 | | __msan_unpoison_string(dest); |
513 | | return result; |
514 | | } |
515 | | static size_t (*libc_strlcat)(char *__restrict, const char *__restrict, size_t); |
516 | | size_t strlcat (char *__restrict dest, const char *restrict src, size_t n) |
517 | | { |
518 | | if (!libc_strlcat) { |
519 | | libc_strlcat = dlsym(RTLD_NEXT, "strlcat"); |
520 | | } |
521 | | size_t result = libc_strlcat(dest, src, n); |
522 | | __msan_unpoison_string(dest); |
523 | | return result; |
524 | | } |
525 | | #endif |