Skip to content

Commit 7824b81

Browse files
committed
Compress interned string table offsets and increase maximum supported buffer size
The interned string buffer is organized as a header + a hash table + a zend_string arena. Hash slots point to the arena, but are represented as 32bit offsets from the buffer, which limits the maximum buffer size to about 4GiB. However zend_strings are 8-byte aligned in the buffer, so we can compress the 3 lower bits. This allows to increase the maximum supported interned string buffer size from 4095 MiB to 16367 MiB.
1 parent ef8dcbd commit 7824b81

File tree

5 files changed

+31
-16
lines changed

5 files changed

+31
-16
lines changed

ext/opcache/ZendAccelerator.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -404,15 +404,15 @@ static inline void accel_unlock_all(void)
404404
#define STRTAB_INVALID_POS 0
405405

406406
#define STRTAB_HASH_TO_SLOT(tab, h) \
407-
((uint32_t*)((char*)(tab) + sizeof(*(tab)) + ((h) & (tab)->nTableMask)))
407+
((zend_string_table_pos_t*)((char*)(tab) + sizeof(*(tab)) + ((h) & (tab)->nTableMask)))
408408
#define STRTAB_STR_TO_POS(tab, s) \
409-
((uint32_t)((char*)s - (char*)(tab)))
409+
((zend_string_table_pos_t)(((char*)s - (char*)(tab))/ZEND_STRING_TABLE_POS_ALIGNMENT))
410410
#define STRTAB_POS_TO_STR(tab, pos) \
411-
((zend_string*)((char*)(tab) + (pos)))
411+
((zend_string*)((char*)(tab) + ((uintptr_t)(pos)*ZEND_STRING_TABLE_POS_ALIGNMENT)))
412412
#define STRTAB_COLLISION(s) \
413-
(*((uint32_t*)((char*)s - sizeof(uint32_t))))
413+
(*((zend_string_table_pos_t*)((char*)s - sizeof(zend_string_table_pos_t))))
414414
#define STRTAB_STR_SIZE(s) \
415-
ZEND_MM_ALIGNED_SIZE_EX(_ZSTR_HEADER_SIZE + ZSTR_LEN(s) + 5, 8)
415+
ZEND_MM_ALIGNED_SIZE_EX(_ZSTR_STRUCT_SIZE(ZSTR_LEN(s)) + sizeof(zend_string_table_pos_t), ZEND_STRING_TABLE_POS_ALIGNMENT)
416416
#define STRTAB_NEXT(s) \
417417
((zend_string*)((char*)(s) + STRTAB_STR_SIZE(s)))
418418

@@ -500,7 +500,7 @@ static zend_always_inline zend_string *accel_find_interned_string(zend_string *s
500500
zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str)
501501
{
502502
zend_ulong h;
503-
uint32_t pos, *hash_slot;
503+
zend_string_table_pos_t pos, *hash_slot;
504504
zend_string *s;
505505

506506
if (UNEXPECTED(file_cache_only)) {
@@ -2842,7 +2842,7 @@ static zend_result zend_accel_init_shm(void)
28422842
} else {
28432843
/* Make sure there is always at least one interned string hash slot,
28442844
* so the table can be queried unconditionally. */
2845-
accel_shared_globals_size = sizeof(zend_accel_shared_globals) + sizeof(uint32_t);
2845+
accel_shared_globals_size = sizeof(zend_accel_shared_globals) + sizeof(zend_string_table_pos_t);
28462846
}
28472847

28482848
accel_shared_globals = zend_shared_alloc(accel_shared_globals_size);
@@ -2869,18 +2869,22 @@ static zend_result zend_accel_init_shm(void)
28692869
hash_size |= (hash_size >> 8);
28702870
hash_size |= (hash_size >> 16);
28712871

2872-
ZCSG(interned_strings).nTableMask = hash_size << 2;
2872+
ZCSG(interned_strings).nTableMask =
2873+
hash_size * sizeof(zend_string_table_pos_t);
28732874
ZCSG(interned_strings).nNumOfElements = 0;
28742875
ZCSG(interned_strings).start =
28752876
(zend_string*)((char*)&ZCSG(interned_strings) +
28762877
sizeof(zend_string_table) +
2877-
((hash_size + 1) * sizeof(uint32_t))) +
2878+
((hash_size + 1) * sizeof(zend_string_table_pos_t))) +
28782879
8;
2880+
ZEND_ASSERT(((uintptr_t)ZCSG(interned_strings).start & 0x7) == 0); /* should be 8 byte aligned */
2881+
28792882
ZCSG(interned_strings).top =
28802883
ZCSG(interned_strings).start;
28812884
ZCSG(interned_strings).end =
28822885
(zend_string*)((char*)(accel_shared_globals + 1) + /* table data is stored after accel_shared_globals */
28832886
ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
2887+
ZEND_ASSERT(((uintptr_t)ZCSG(interned_strings).end - (uintptr_t)&ZCSG(interned_strings)) / ZEND_STRING_TABLE_POS_ALIGNMENT < ZEND_STRING_TABLE_POS_MAX);
28842888
ZCSG(interned_strings).saved_top = NULL;
28852889

28862890
memset((char*)&ZCSG(interned_strings) + sizeof(zend_string_table),

ext/opcache/ZendAccelerator.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,11 @@ typedef struct _zend_string_table {
236236
zend_string *saved_top;
237237
} zend_string_table;
238238

239+
typedef uint32_t zend_string_table_pos_t;
240+
241+
#define ZEND_STRING_TABLE_POS_MAX UINT32_MAX
242+
#define ZEND_STRING_TABLE_POS_ALIGNMENT 8
243+
239244
typedef struct _zend_accel_shared_globals {
240245
/* Cache Data Structures */
241246
zend_ulong hits;

ext/opcache/tests/gh9259_001.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ echo 'OK';
1313

1414
?>
1515
--EXPECTF--
16-
%sWarning opcache.interned_strings_buffer must be less than or equal to 4095, 131072 given%s
16+
%sWarning opcache.interned_strings_buffer must be less than or equal to 32767, 131072 given%s
1717

1818
OK

ext/opcache/zend_accelerator_module.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,17 @@
4141
#define STRING_NOT_NULL(s) (NULL == (s)?"":s)
4242
#define MIN_ACCEL_FILES 200
4343
#define MAX_ACCEL_FILES 1000000
44-
#define MAX_INTERNED_STRINGS_BUFFER_SIZE ((zend_long)((UINT32_MAX-PLATFORM_ALIGNMENT-sizeof(zend_accel_shared_globals))/(1024*1024)))
44+
/* Max value of opcache.interned_strings_buffer */
45+
#define MAX_INTERNED_STRINGS_BUFFER_SIZE ((zend_long)MIN( \
46+
MIN( \
47+
/* STRTAB_STR_TO_POS() must not overflow zend_string_table_pos_t */ \
48+
(ZEND_STRING_TABLE_POS_MAX-sizeof(zend_string_table))/(1024*1024/ZEND_STRING_TABLE_POS_ALIGNMENT), \
49+
/* nTableMask must not overflow (uint32_t) */ \
50+
UINT32_MAX/(32*1024*sizeof(zend_string_table_pos_t)) \
51+
), \
52+
/* SHM allocation must not overlow (size_t) */ \
53+
(SIZE_MAX-sizeof(zend_accel_shared_globals))/(1024*1024) \
54+
))
4555
#define TOKENTOSTR(X) #X
4656

4757
static zif_handler orig_file_exists = NULL;

ext/opcache/zend_shared_alloc.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -369,11 +369,7 @@ void *zend_shared_alloc(size_t size)
369369
ZEND_ASSERT(ZCG(locked));
370370

371371
int i;
372-
unsigned int block_size = ZEND_ALIGNED_SIZE(size);
373-
374-
if (UNEXPECTED(block_size < size)) {
375-
zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Possible integer overflow in shared memory allocation (%zu + %zu)", size, PLATFORM_ALIGNMENT);
376-
}
372+
size_t block_size = ZEND_ALIGNED_SIZE(size);
377373

378374
if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */
379375
SHARED_ALLOC_FAILED();

0 commit comments

Comments
 (0)