Skip to content

Commit 45846cc

Browse files
committed
WIP: Implement a soft memory limit
1 parent d58e3c0 commit 45846cc

File tree

6 files changed

+67
-5
lines changed

6 files changed

+67
-5
lines changed

Zend/zend_alloc.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "zend_multiply.h"
5959
#include "zend_bitset.h"
6060
#include "zend_mmap.h"
61+
#include "zend_exceptions.h"
6162
#include <signal.h>
6263

6364
#ifdef HAVE_UNISTD_H
@@ -251,6 +252,7 @@ struct _zend_mm_heap {
251252
#endif
252253
#if ZEND_MM_LIMIT
253254
size_t limit; /* memory limit */
255+
size_t soft_limit; /* soft memory limit */
254256
int overflow; /* memory overflow flag */
255257
#endif
256258

@@ -1017,6 +1019,14 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, uint32_t pages_count ZEND_F
10171019
return NULL;
10181020
}
10191021
}
1022+
1023+
if (UNEXPECTED(ZEND_MM_CHUNK_SIZE > heap->soft_limit - heap->real_size)) {
1024+
#if ZEND_DEBUG
1025+
zend_throw_exception_ex(zend_ce_oom_error, 0, "Allowed memory size of %zu bytes exhausted at %s:%d (tried to allocate %zu bytes)", heap->soft_limit, __zend_filename, __zend_lineno, size);
1026+
#else
1027+
zend_throw_exception_ex(zend_ce_oom_error, 0, "Allowed memory size of %zu bytes exhausted (tried to allocate %zu bytes)", heap->soft_limit, ZEND_MM_PAGE_SIZE * pages_count);
1028+
#endif
1029+
}
10201030
#endif
10211031
chunk = (zend_mm_chunk*)zend_mm_chunk_alloc(heap, ZEND_MM_CHUNK_SIZE, ZEND_MM_CHUNK_SIZE);
10221032
if (UNEXPECTED(chunk == NULL)) {
@@ -1543,6 +1553,14 @@ static zend_never_inline void *zend_mm_realloc_huge(zend_mm_heap *heap, void *pt
15431553
return NULL;
15441554
}
15451555
}
1556+
1557+
if (UNEXPECTED(new_size - old_size > heap->soft_limit - heap->real_size)) {
1558+
#if ZEND_DEBUG
1559+
zend_throw_exception_ex(zend_ce_oom_error, 0, "Allowed memory size of %zu bytes exhausted at %s:%d (tried to allocate %zu bytes)", heap->soft_limit, __zend_filename, __zend_lineno, size);
1560+
#else
1561+
zend_throw_exception_ex(zend_ce_oom_error, 0, "Allowed memory size of %zu bytes exhausted (tried to allocate %zu bytes)", heap->soft_limit, size);
1562+
#endif
1563+
}
15461564
#endif
15471565
/* try to map tail right after this block */
15481566
if (zend_mm_chunk_extend(heap, ptr, old_size, new_size)) {
@@ -1835,6 +1853,9 @@ static void *zend_mm_alloc_huge(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
18351853
return NULL;
18361854
}
18371855
}
1856+
if (UNEXPECTED(new_size > heap->soft_limit - heap->real_size)) {
1857+
zend_throw_exception_ex(zend_ce_error, 0, "Allowed memory size of %zu bytes exhausted", heap->soft_limit);
1858+
}
18381859
#endif
18391860
ptr = zend_mm_chunk_alloc(heap, new_size, ZEND_MM_CHUNK_SIZE);
18401861
if (UNEXPECTED(ptr == NULL)) {
@@ -2734,6 +2755,7 @@ ZEND_API zend_result zend_set_memory_limit(size_t memory_limit)
27342755
return FAILURE;
27352756
}
27362757
AG(mm_heap)->limit = memory_limit;
2758+
AG(mm_heap)->soft_limit = 3 * 1024 * 1024;
27372759
#endif
27382760
return SUCCESS;
27392761
}
@@ -2817,6 +2839,14 @@ static zend_always_inline void tracked_check_limit(zend_mm_heap *heap, size_t ad
28172839
zend_mm_safe_error(heap,
28182840
"Allowed memory size of %zu bytes exhausted (tried to allocate %zu bytes)",
28192841
heap->limit, add_size);
2842+
#endif
2843+
}
2844+
2845+
if (add_size > heap->soft_limit - heap->size) {
2846+
#if ZEND_DEBUG
2847+
zend_throw_exception_ex(zend_ce_error, 0, "Allowed memory size of %zu bytes exhausted at %s:%d (tried to allocate %zu bytes)", heap->soft_limit, "file", 0, add_size);
2848+
#else
2849+
zend_throw_exception_ex(zend_ce_error, 0, "Allowed memory size of %zu bytes exhausted (tried to allocate %zu bytes)", heap->soft_limit, add_size);
28202850
#endif
28212851
}
28222852
}

Zend/zend_exceptions.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ ZEND_API zend_class_entry *zend_ce_value_error;
4242
ZEND_API zend_class_entry *zend_ce_arithmetic_error;
4343
ZEND_API zend_class_entry *zend_ce_division_by_zero_error;
4444
ZEND_API zend_class_entry *zend_ce_unhandled_match_error;
45+
ZEND_API zend_class_entry *zend_ce_oom_error;
4546

4647
/* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does not* execute finally blocks. */
4748
static zend_class_entry zend_ce_unwind_exit;
@@ -794,6 +795,9 @@ void zend_register_default_exception(void) /* {{{ */
794795
zend_ce_unhandled_match_error = register_class_UnhandledMatchError(zend_ce_error);
795796
zend_init_exception_class_entry(zend_ce_unhandled_match_error);
796797

798+
zend_ce_oom_error = register_class_OomError(zend_ce_error);
799+
zend_init_exception_class_entry(zend_ce_oom_error);
800+
797801
INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL);
798802

799803
INIT_CLASS_ENTRY(zend_ce_graceful_exit, "GracefulExit", NULL);

Zend/zend_exceptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ extern ZEND_API zend_class_entry *zend_ce_value_error;
3636
extern ZEND_API zend_class_entry *zend_ce_arithmetic_error;
3737
extern ZEND_API zend_class_entry *zend_ce_division_by_zero_error;
3838
extern ZEND_API zend_class_entry *zend_ce_unhandled_match_error;
39+
extern ZEND_API zend_class_entry *zend_ce_oom_error;
3940

4041
ZEND_API void zend_exception_set_previous(zend_object *exception, zend_object *add_previous);
4142
ZEND_API void zend_exception_save(void);

Zend/zend_exceptions.stub.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,7 @@ class DivisionByZeroError extends ArithmeticError
170170
class UnhandledMatchError extends Error
171171
{
172172
}
173+
174+
class OomError extends Error
175+
{
176+
}

Zend/zend_exceptions_arginfo.h

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/json/json_parser.y

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,23 +236,31 @@ static int php_json_parser_object_create(php_json_parser *parser, zval *object)
236236

237237
static int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue)
238238
{
239+
if (EG(exception)) {
240+
goto fail;
241+
}
242+
239243
/* if JSON_OBJECT_AS_ARRAY is set */
240244
if (Z_TYPE_P(object) == IS_ARRAY) {
241245
zend_symtable_update(Z_ARRVAL_P(object), key, zvalue);
242246
} else {
243247
if (ZSTR_LEN(key) > 0 && ZSTR_VAL(key)[0] == '\0') {
244248
parser->scanner.errcode = PHP_JSON_ERROR_INVALID_PROPERTY_NAME;
245-
zend_string_release_ex(key, 0);
246-
zval_ptr_dtor_nogc(zvalue);
247-
zval_ptr_dtor_nogc(object);
248-
return FAILURE;
249+
goto fail;
249250
}
250251
zend_std_write_property(Z_OBJ_P(object), key, zvalue, NULL);
251252
Z_TRY_DELREF_P(zvalue);
252253
}
253254
zend_string_release_ex(key, 0);
254255

255256
return SUCCESS;
257+
258+
fail:
259+
zend_string_release_ex(key, 0);
260+
zval_ptr_dtor_nogc(zvalue);
261+
zval_ptr_dtor_nogc(object);
262+
263+
return FAILURE;
256264
}
257265

258266
static int php_json_parser_array_create_validate(php_json_parser *parser, zval *array)

0 commit comments

Comments
 (0)