Skip to content

BCMath: Avoid using the heap for temporary objects by using arena allocations #14170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 8, 2024
Merged
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
47 changes: 47 additions & 0 deletions ext/bcmath/bcmath.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@
#include "php_bcmath.h"
#include "libbcmath/src/bcmath.h"

/* Always pair SETUP with TEARDOWN, and do so in the outer scope!
* Should not be used when data can escape the function. */
#define BC_ARENA_SETUP \
char bc_arena[BC_ARENA_SIZE]; \
BCG(arena) = bc_arena;

#define BC_ARENA_TEARDOWN \
BCG(arena) = NULL; \
BCG(arena_offset) = 0;

ZEND_DECLARE_MODULE_GLOBALS(bcmath)
static PHP_GINIT_FUNCTION(bcmath);
static PHP_GSHUTDOWN_FUNCTION(bcmath);
Expand Down Expand Up @@ -89,6 +99,8 @@ static PHP_GINIT_FUNCTION(bcmath)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
bcmath_globals->bc_precision = 0;
bcmath_globals->arena = NULL;
bcmath_globals->arena_offset = 0;
bc_init_numbers();
}
/* }}} */
Expand All @@ -99,6 +111,8 @@ static PHP_GSHUTDOWN_FUNCTION(bcmath)
_bc_free_num_ex(&bcmath_globals->_zero_, 1);
_bc_free_num_ex(&bcmath_globals->_one_, 1);
_bc_free_num_ex(&bcmath_globals->_two_, 1);
bcmath_globals->arena = NULL;
bcmath_globals->arena_offset = 0;
}
/* }}} */

Expand Down Expand Up @@ -167,6 +181,8 @@ PHP_FUNCTION(bcadd)
scale = (int) scale_param;
}

BC_ARENA_SETUP;

if (php_str2num(&first, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
Expand All @@ -185,6 +201,7 @@ PHP_FUNCTION(bcadd)
bc_free_num(&first);
bc_free_num(&second);
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down Expand Up @@ -214,6 +231,8 @@ PHP_FUNCTION(bcsub)
scale = (int) scale_param;
}

BC_ARENA_SETUP;

if (php_str2num(&first, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
Expand All @@ -232,6 +251,7 @@ PHP_FUNCTION(bcsub)
bc_free_num(&first);
bc_free_num(&second);
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down Expand Up @@ -261,6 +281,8 @@ PHP_FUNCTION(bcmul)
scale = (int) scale_param;
}

BC_ARENA_SETUP;

if (php_str2num(&first, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
Expand All @@ -279,6 +301,7 @@ PHP_FUNCTION(bcmul)
bc_free_num(&first);
bc_free_num(&second);
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down Expand Up @@ -308,6 +331,8 @@ PHP_FUNCTION(bcdiv)
scale = (int) scale_param;
}

BC_ARENA_SETUP;

bc_init_num(&result);

if (php_str2num(&first, left) == FAILURE) {
Expand All @@ -331,6 +356,7 @@ PHP_FUNCTION(bcdiv)
bc_free_num(&first);
bc_free_num(&second);
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down Expand Up @@ -360,6 +386,8 @@ PHP_FUNCTION(bcmod)
scale = (int) scale_param;
}

BC_ARENA_SETUP;

bc_init_num(&result);

if (php_str2num(&first, left) == FAILURE) {
Expand All @@ -383,6 +411,7 @@ PHP_FUNCTION(bcmod)
bc_free_num(&first);
bc_free_num(&second);
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down Expand Up @@ -413,6 +442,8 @@ PHP_FUNCTION(bcpowmod)
scale = (int) scale_param;
}

BC_ARENA_SETUP;

bc_init_num(&result);

if (php_str2num(&bc_base, base_str) == FAILURE) {
Expand Down Expand Up @@ -458,6 +489,7 @@ PHP_FUNCTION(bcpowmod)
bc_free_num(&bc_expo);
bc_free_num(&bc_modulus);
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down Expand Up @@ -487,6 +519,8 @@ PHP_FUNCTION(bcpow)
scale = (int) scale_param;
}

BC_ARENA_SETUP;

bc_init_num(&result);

if (php_str2num(&first, base_str) == FAILURE) {
Expand Down Expand Up @@ -518,6 +552,7 @@ PHP_FUNCTION(bcpow)
bc_free_num(&first);
bc_free_num(&bc_exponent);
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down Expand Up @@ -546,6 +581,8 @@ PHP_FUNCTION(bcsqrt)
scale = (int) scale_param;
}

BC_ARENA_SETUP;

if (php_str2num(&result, left) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
Expand All @@ -559,6 +596,7 @@ PHP_FUNCTION(bcsqrt)

cleanup: {
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down Expand Up @@ -588,6 +626,8 @@ PHP_FUNCTION(bccomp)
scale = (int) scale_param;
}

BC_ARENA_SETUP;

if (!bc_str2num(&first, ZSTR_VAL(left), ZSTR_VAL(left) + ZSTR_LEN(left), scale, false)) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
Expand All @@ -603,6 +643,7 @@ PHP_FUNCTION(bccomp)
cleanup: {
bc_free_num(&first);
bc_free_num(&second);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand All @@ -617,6 +658,8 @@ static void bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAMETERS, bool is_floor)
Z_PARAM_STR(numstr)
ZEND_PARSE_PARAMETERS_END();

BC_ARENA_SETUP;

if (php_str2num(&num, numstr) == FAILURE) {
zend_argument_value_error(1, "is not well-formed");
goto cleanup;
Expand All @@ -628,6 +671,7 @@ static void bcfloor_or_bcceil(INTERNAL_FUNCTION_PARAMETERS, bool is_floor)
cleanup: {
bc_free_num(&num);
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down Expand Up @@ -676,6 +720,8 @@ PHP_FUNCTION(bcround)
return;
}

BC_ARENA_SETUP;

bc_init_num(&result);

if (php_str2num(&num, numstr) == FAILURE) {
Expand All @@ -689,6 +735,7 @@ PHP_FUNCTION(bcround)
cleanup: {
bc_free_num(&num);
bc_free_num(&result);
BC_ARENA_TEARDOWN;
};
}
/* }}} */
Expand Down
19 changes: 15 additions & 4 deletions ext/bcmath/libbcmath/src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,25 @@
#include <string.h>
#include "zend_alloc.h"

static zend_always_inline bc_num _bc_new_num_nonzeroed_ex_internal(size_t length, size_t scale, bool persistent)
static bc_num _bc_new_num_nonzeroed_ex_internal(size_t length, size_t scale, bool persistent)
{
/* PHP Change: malloc() -> pemalloc(), removed free_list code, merged n_ptr and n_value */
bc_num temp = safe_pemalloc(1, sizeof(bc_struct) + length, scale, persistent);
size_t required_size = zend_safe_address_guarded(1, sizeof(bc_struct) + (ZEND_MM_ALIGNMENT - 1) + length, scale);
required_size &= -ZEND_MM_ALIGNMENT;
bc_num temp;

if (!persistent && BCG(arena) && required_size <= BC_ARENA_SIZE - BCG(arena_offset)) {
temp = (bc_num) (BCG(arena) + BCG(arena_offset));
BCG(arena_offset) += required_size;
temp->n_refs = 2; /* prevent freeing */
} else {
/* PHP Change: malloc() -> pemalloc(), removed free_list code, merged n_ptr and n_value */
temp = pemalloc(required_size, persistent);
temp->n_refs = 1;
}

temp->n_sign = PLUS;
temp->n_len = length;
temp->n_scale = scale;
temp->n_refs = 1;
temp->n_value = (char *) temp + sizeof(bc_struct);
return temp;
}
Expand Down
4 changes: 4 additions & 0 deletions ext/bcmath/php_bcmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,15 @@ extern zend_module_entry bcmath_module_entry;
#include "php_version.h"
#define PHP_BCMATH_VERSION PHP_VERSION

#define BC_ARENA_SIZE 256

ZEND_BEGIN_MODULE_GLOBALS(bcmath)
bc_num _zero_;
bc_num _one_;
bc_num _two_;
int bc_precision;
char *arena;
size_t arena_offset;
ZEND_END_MODULE_GLOBALS(bcmath)

#if defined(ZTS) && defined(COMPILE_DL_BCMATH)
Expand Down
Loading