Skip to content

Commit fb242f4

Browse files
authored
Fix high opcache.interned_strings_buffer causing shm corruption (php#9260)
1 parent 5210872 commit fb242f4

File tree

6 files changed

+85
-5
lines changed

6 files changed

+85
-5
lines changed

ext/opcache/ZendAccelerator.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,18 +2840,23 @@ static inline int accel_find_sapi(void)
28402840
static int zend_accel_init_shm(void)
28412841
{
28422842
int i;
2843+
size_t accel_shared_globals_size;
28432844

28442845
zend_shared_alloc_lock();
28452846

28462847
if (ZCG(accel_directives).interned_strings_buffer) {
2847-
accel_shared_globals = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024));
2848+
accel_shared_globals_size = ZCG(accel_directives).interned_strings_buffer * 1024 * 1024;
28482849
} else {
28492850
/* Make sure there is always at least one interned string hash slot,
28502851
* so the table can be queried unconditionally. */
2851-
accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals) + sizeof(uint32_t));
2852+
accel_shared_globals_size = sizeof(zend_accel_shared_globals) + sizeof(uint32_t);
28522853
}
2854+
2855+
accel_shared_globals = zend_shared_alloc(accel_shared_globals_size);
28532856
if (!accel_shared_globals) {
2854-
zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
2857+
zend_accel_error_noreturn(ACCEL_LOG_FATAL,
2858+
"Insufficient shared memory for interned strings buffer! (tried to allocate %zu bytes)",
2859+
accel_shared_globals_size);
28552860
zend_shared_alloc_unlock();
28562861
return FAILURE;
28572862
}

ext/opcache/tests/gh9259_001.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Bug GH-9259 001 (Setting opcache.interned_strings_buffer to a very high value leads to corruption of shm)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.interned_strings_buffer=131072
7+
opcache.log_verbosity_level=2
8+
opcache.enable_cli=1
9+
--FILE--
10+
<?php
11+
12+
echo 'OK';
13+
14+
?>
15+
--EXPECTF--
16+
%sWarning opcache.interned_strings_buffer must be less than or equal to 4095, 131072 given%s
17+
18+
OK

ext/opcache/tests/gh9259_002.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Bug GH-9259 002 (Setting opcache.interned_strings_buffer to a very high value leads to corruption of shm)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.interned_strings_buffer=-1
7+
opcache.log_verbosity_level=2
8+
opcache.enable_cli=1
9+
--FILE--
10+
<?php
11+
12+
echo 'OK';
13+
14+
?>
15+
--EXPECTF--
16+
%sWarning opcache.interned_strings_buffer must be greater than or equal to 0, -1 given%s
17+
18+
OK

ext/opcache/tests/gh9259_003.phpt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Bug GH-9259 003 (Setting opcache.interned_strings_buffer to a very high value leads to corruption of shm)
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.interned_strings_buffer=500
7+
opcache.enable_cli=1
8+
--FILE--
9+
<?php
10+
11+
echo 'OK';
12+
13+
?>
14+
--EXPECTF--
15+
%sFatal Error Insufficient shared memory for interned strings buffer! (tried to allocate %d bytes)

ext/opcache/zend_accelerator_module.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#define STRING_NOT_NULL(s) (NULL == (s)?"":s)
4141
#define MIN_ACCEL_FILES 200
4242
#define MAX_ACCEL_FILES 1000000
43+
#define MAX_INTERNED_STRINGS_BUFFER_SIZE ((zend_long)((UINT32_MAX-PLATFORM_ALIGNMENT)/(1024*1024)))
4344
#define TOKENTOSTR(X) #X
4445

4546
static zif_handler orig_file_exists = NULL;
@@ -78,6 +79,25 @@ static ZEND_INI_MH(OnUpdateMemoryConsumption)
7879
return SUCCESS;
7980
}
8081

82+
static ZEND_INI_MH(OnUpdateInternedStringsBuffer)
83+
{
84+
zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
85+
zend_long size = zend_ini_parse_quantity_warn(new_value, entry->name);
86+
87+
if (size < 0) {
88+
zend_accel_error(ACCEL_LOG_WARNING, "opcache.interned_strings_buffer must be greater than or equal to 0, " ZEND_LONG_FMT " given.\n", size);
89+
return FAILURE;
90+
}
91+
if (size > MAX_INTERNED_STRINGS_BUFFER_SIZE) {
92+
zend_accel_error(ACCEL_LOG_WARNING, "opcache.interned_strings_buffer must be less than or equal to " ZEND_LONG_FMT ", " ZEND_LONG_FMT " given.\n", MAX_INTERNED_STRINGS_BUFFER_SIZE, size);
93+
return FAILURE;
94+
}
95+
96+
*p = size;
97+
98+
return SUCCESS;
99+
}
100+
81101
static ZEND_INI_MH(OnUpdateMaxAcceleratedFiles)
82102
{
83103
zend_long *p = (zend_long *) ZEND_INI_GET_ADDR();
@@ -239,7 +259,7 @@ ZEND_INI_BEGIN()
239259

240260
STD_PHP_INI_ENTRY("opcache.log_verbosity_level" , "1" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.log_verbosity_level, zend_accel_globals, accel_globals)
241261
STD_PHP_INI_ENTRY("opcache.memory_consumption" , "128" , PHP_INI_SYSTEM, OnUpdateMemoryConsumption, accel_directives.memory_consumption, zend_accel_globals, accel_globals)
242-
STD_PHP_INI_ENTRY("opcache.interned_strings_buffer", "8" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.interned_strings_buffer, zend_accel_globals, accel_globals)
262+
STD_PHP_INI_ENTRY("opcache.interned_strings_buffer", "8" , PHP_INI_SYSTEM, OnUpdateInternedStringsBuffer, accel_directives.interned_strings_buffer, zend_accel_globals, accel_globals)
243263
STD_PHP_INI_ENTRY("opcache.max_accelerated_files" , "10000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles, accel_directives.max_accelerated_files, zend_accel_globals, accel_globals)
244264
STD_PHP_INI_ENTRY("opcache.max_wasted_percentage" , "5" , PHP_INI_SYSTEM, OnUpdateMaxWastedPercentage, accel_directives.max_wasted_percentage, zend_accel_globals, accel_globals)
245265
STD_PHP_INI_ENTRY("opcache.consistency_checks" , "0" , PHP_INI_ALL , OnUpdateLong, accel_directives.consistency_checks, zend_accel_globals, accel_globals)

ext/opcache/zend_shared_alloc.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ static size_t zend_shared_alloc_get_largest_free_block(void)
328328
#define MIN_FREE_MEMORY 64*1024
329329

330330
#define SHARED_ALLOC_FAILED() do { \
331-
zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate "ZEND_LONG_FMT" bytes ("ZEND_LONG_FMT" bytes free)", (zend_long)size, (zend_long)ZSMMG(shared_free)); \
331+
zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate %zu bytes (%zu bytes free)", size, ZSMMG(shared_free)); \
332332
if (zend_shared_alloc_get_largest_free_block() < MIN_FREE_MEMORY) { \
333333
ZSMMG(memory_exhausted) = 1; \
334334
} \
@@ -339,6 +339,10 @@ void *zend_shared_alloc(size_t size)
339339
int i;
340340
unsigned int block_size = ZEND_ALIGNED_SIZE(size);
341341

342+
if (UNEXPECTED(block_size < size)) {
343+
zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Possible integer overflow in shared memory allocation (%zu + %zu)", size, PLATFORM_ALIGNMENT);
344+
}
345+
342346
#if 1
343347
if (!ZCG(locked)) {
344348
zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Shared memory lock not obtained");

0 commit comments

Comments
 (0)