Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Zend/zend_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ static zend_always_inline bool ZSTR_IS_INTERNED(const zend_string *s) {
}

static inline bool ZSTR_IS_VALID_UTF8(const zend_string *s) {
return GC_FLAGS(s) & IS_STR_VALID_UTF8;
/* If string is persistent the check for flags must be atomic.
* Not checking for persistence first, as this operation must also be run atomic.
* So uses atomic operation directly to check valid utf8 instead. */
return GC_FLAGS_ATOMIC(s) & IS_STR_VALID_UTF8;
}

/* These are properties, encoded as flags, that will hold on the resulting string
Expand Down
43 changes: 43 additions & 0 deletions Zend/zend_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,49 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
GC_TYPE_INFO(p) &= ~((flags) << GC_FLAGS_SHIFT); \
} while (0)

#if defined(__cplusplus) || !defined(ATOMIC_INT_LOCK_FREE) || ATOMIC_INT_LOCK_FREE != 2
/* Fallback path for C++ compilation units or non-lock-free architectures */
#if defined(_MSC_VER)
#include <intrin.h>
#define GC_FLAGS_ATOMIC(p) zval_gc_flags(*(const volatile uint32_t *)&GC_TYPE_INFO(p))
#define GC_ADD_FLAGS_ATOMIC(p, flags) do { \
_InterlockedOr((volatile LONG *)&GC_TYPE_INFO(p), ((uint32_t)(flags)) << GC_FLAGS_SHIFT); \
} while (0)
#else
#define GC_FLAGS_ATOMIC(p) zval_gc_flags(*(const volatile uint32_t *)&GC_TYPE_INFO(p))
#define GC_ADD_FLAGS_ATOMIC(p, flags) do { \
uint32_t _old = *(volatile uint32_t *)&GC_TYPE_INFO(p); \
uint32_t _new; \
do { \
_new = _old | (((uint32_t)(flags)) << GC_FLAGS_SHIFT); \
} while (!_VAL_ATOMIC_CAS(&GC_TYPE_INFO(p), &_old, _new)); \
} while (0)
#endif
#else
/* Native C11 lock-free atomic path */
#if defined(_MSC_VER)
#include <intrin.h>
#define GC_FLAGS_ATOMIC(p) zval_gc_flags(*(const volatile uint32_t *)&GC_TYPE_INFO(p))
#define GC_ADD_FLAGS_ATOMIC(p, flags) \
_InterlockedOr((volatile LONG *)&GC_TYPE_INFO(p), ((uint32_t)(flags)) << GC_FLAGS_SHIFT)
#elif defined(__GNUC__) || defined(__clang__)
#define GC_FLAGS_ATOMIC(p) zval_gc_flags(__atomic_load_n(&GC_TYPE_INFO(p), __ATOMIC_RELAXED))
#define GC_ADD_FLAGS_ATOMIC(p, flags) \
__atomic_fetch_or(&GC_TYPE_INFO(p), ((uint32_t)(flags)) << GC_FLAGS_SHIFT, __ATOMIC_RELAXED)
#else
#include <stdatomic.h>
#define GC_FLAGS_ATOMIC(p) zval_gc_flags( \
atomic_load_explicit((_Atomic(uint32_t) *)&GC_TYPE_INFO(p), memory_order_relaxed) \
)
#define GC_ADD_FLAGS_ATOMIC(p, flags) \
atomic_fetch_or_explicit( \
(_Atomic(uint32_t) *)&GC_TYPE_INFO(p), \
((uint32_t)(flags)) << GC_FLAGS_SHIFT, \
memory_order_relaxed \
)
#endif
#endif

#ifndef ZEND_RC_DEBUG
# define ZEND_RC_DEBUG 0
#endif
Expand Down
28 changes: 21 additions & 7 deletions ext/mbstring/mbstring.c
Original file line number Diff line number Diff line change
Expand Up @@ -2246,7 +2246,9 @@ static zend_string* mb_get_substr(zend_string *input, size_t from, size_t len, c
}
}
zend_string *result = zend_string_init_fast((const char*)start, stop - start);
if (!ZSTR_IS_INTERNED(result)) {
if (ZSTR_IS_INTERNED(result)) {
GC_ADD_FLAGS_ATOMIC(result, IS_STR_VALID_UTF8);
} else {
GC_ADD_FLAGS(result, IS_STR_VALID_UTF8);
}
return result;
Expand Down Expand Up @@ -2364,8 +2366,12 @@ PHP_FUNCTION(mb_substr_count)
} else {
unsigned int num_errors = 0;
haystack_u8 = mb_fast_convert((unsigned char*)ZSTR_VAL(haystack), ZSTR_LEN(haystack), enc, &mbfl_encoding_utf8, 0, MBFL_OUTPUTFILTER_ILLEGAL_MODE_BADUTF8, &num_errors);
if (!num_errors && !ZSTR_IS_INTERNED(haystack)) {
GC_ADD_FLAGS(haystack, IS_STR_VALID_UTF8);
if (!num_errors) {
if (ZSTR_IS_INTERNED(haystack)) {
GC_ADD_FLAGS_ATOMIC(haystack, IS_STR_VALID_UTF8);
} else {
GC_ADD_FLAGS(haystack, IS_STR_VALID_UTF8);
}
}
}

Expand All @@ -2374,8 +2380,12 @@ PHP_FUNCTION(mb_substr_count)
} else {
unsigned int num_errors = 0;
needle_u8 = mb_fast_convert((unsigned char*)ZSTR_VAL(needle), ZSTR_LEN(needle), enc, &mbfl_encoding_utf8, 0, MBFL_OUTPUTFILTER_ILLEGAL_MODE_BADUTF8, &num_errors);
if (!num_errors && !ZSTR_IS_INTERNED(needle)) {
GC_ADD_FLAGS(needle, IS_STR_VALID_UTF8);
if (!num_errors) {
if (ZSTR_IS_INTERNED(needle)) {
GC_ADD_FLAGS_ATOMIC(needle, IS_STR_VALID_UTF8);
} else {
GC_ADD_FLAGS(needle, IS_STR_VALID_UTF8);
}
}
}
} else {
Expand Down Expand Up @@ -5756,8 +5766,12 @@ static bool mb_check_str_encoding(zend_string *str, const mbfl_encoding *encodin
return true;
}
bool result = mb_fast_check_utf8(str);
if (result && !ZSTR_IS_INTERNED(str)) {
GC_ADD_FLAGS(str, IS_STR_VALID_UTF8);
if (result) {
if (ZSTR_IS_INTERNED(str)) {
GC_ADD_FLAGS_ATOMIC(str, IS_STR_VALID_UTF8);
} else if(!ZSTR_IS_INTERNED(str)) {
GC_ADD_FLAGS(str, IS_STR_VALID_UTF8);
}
}
return result;
} else {
Expand Down
9 changes: 6 additions & 3 deletions ext/pcre/php_pcre.c
Original file line number Diff line number Diff line change
Expand Up @@ -1508,9 +1508,12 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, zend_string *subject_str,

if (PCRE_G(error_code) == PHP_PCRE_NO_ERROR) {
/* If there was no error and we're in /u mode, remember that the string is valid UTF-8. */
if ((pce->compile_options & PCRE2_UTF)
&& !ZSTR_IS_INTERNED(subject_str) && orig_start_offset == 0) {
GC_ADD_FLAGS(subject_str, IS_STR_VALID_UTF8);
if ((pce->compile_options & PCRE2_UTF) && orig_start_offset == 0) {
if (ZSTR_IS_INTERNED(subject_str)) {
GC_ADD_FLAGS_ATOMIC(subject_str, IS_STR_VALID_UTF8);
} else {
GC_ADD_FLAGS(subject_str, IS_STR_VALID_UTF8);
}
}

RETVAL_LONG(matched);
Expand Down
Loading