Skip to content

Commit e16fa76

Browse files
committed
Changed to run bc_free_num() before bc_num2str_ex() whenever possible
When using `BCG`, when memory is exhausted, the refcount may not decrease as expected and a memory leak may occur.
1 parent c42f48d commit e16fa76

File tree

2 files changed

+78
-34
lines changed

2 files changed

+78
-34
lines changed

ext/bcmath/bcmath.c

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -183,23 +183,28 @@ PHP_FUNCTION(bcadd)
183183

184184
if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
185185
zend_argument_value_error(1, "is not well-formed");
186-
goto cleanup;
186+
goto fail;
187187
}
188188

189189
if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
190190
zend_argument_value_error(2, "is not well-formed");
191-
goto cleanup;
191+
goto fail;
192192
}
193193

194194
bc_add (first, second, &result, scale);
195195

196+
bc_free_num(&first);
197+
bc_free_num(&second);
196198
RETVAL_STR(bc_num2str_ex(result, scale));
197199

198-
cleanup: {
200+
bc_free_num(&result);
201+
return;
202+
203+
fail: {
199204
bc_free_num(&first);
200205
bc_free_num(&second);
201206
bc_free_num(&result);
202-
};
207+
}
203208
}
204209
/* }}} */
205210

@@ -234,23 +239,28 @@ PHP_FUNCTION(bcsub)
234239

235240
if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
236241
zend_argument_value_error(1, "is not well-formed");
237-
goto cleanup;
242+
goto fail;
238243
}
239244

240245
if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
241246
zend_argument_value_error(2, "is not well-formed");
242-
goto cleanup;
247+
goto fail;
243248
}
244249

245250
bc_sub (first, second, &result, scale);
246251

252+
bc_free_num(&first);
253+
bc_free_num(&second);
247254
RETVAL_STR(bc_num2str_ex(result, scale));
248255

249-
cleanup: {
256+
bc_free_num(&result);
257+
return;
258+
259+
fail: {
250260
bc_free_num(&first);
251261
bc_free_num(&second);
252262
bc_free_num(&result);
253-
};
263+
}
254264
}
255265
/* }}} */
256266

@@ -285,19 +295,24 @@ PHP_FUNCTION(bcmul)
285295

286296
if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
287297
zend_argument_value_error(1, "is not well-formed");
288-
goto cleanup;
298+
goto fail;
289299
}
290300

291301
if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
292302
zend_argument_value_error(2, "is not well-formed");
293-
goto cleanup;
303+
goto fail;
294304
}
295305

296306
bc_multiply (first, second, &result, scale);
297307

308+
bc_free_num(&first);
309+
bc_free_num(&second);
298310
RETVAL_STR(bc_num2str_ex(result, scale));
299311

300-
cleanup: {
312+
bc_free_num(&result);
313+
return;
314+
315+
fail: {
301316
bc_free_num(&first);
302317
bc_free_num(&second);
303318
bc_free_num(&result);
@@ -336,22 +351,27 @@ PHP_FUNCTION(bcdiv)
336351

337352
if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
338353
zend_argument_value_error(1, "is not well-formed");
339-
goto cleanup;
354+
goto fail;
340355
}
341356

342357
if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
343358
zend_argument_value_error(2, "is not well-formed");
344-
goto cleanup;
359+
goto fail;
345360
}
346361

347362
if (!bc_divide(first, second, &result, scale)) {
348363
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Division by zero");
349-
goto cleanup;
364+
goto fail;
350365
}
351366

367+
bc_free_num(&first);
368+
bc_free_num(&second);
352369
RETVAL_STR(bc_num2str_ex(result, scale));
353370

354-
cleanup: {
371+
bc_free_num(&result);
372+
return;
373+
374+
fail: {
355375
bc_free_num(&first);
356376
bc_free_num(&second);
357377
bc_free_num(&result);
@@ -390,22 +410,27 @@ PHP_FUNCTION(bcmod)
390410

391411
if (php_str2num(&first, ZSTR_VAL(left)) == FAILURE) {
392412
zend_argument_value_error(1, "is not well-formed");
393-
goto cleanup;
413+
goto fail;
394414
}
395415

396416
if (php_str2num(&second, ZSTR_VAL(right)) == FAILURE) {
397417
zend_argument_value_error(2, "is not well-formed");
398-
goto cleanup;
418+
goto fail;
399419
}
400420

401421
if (!bc_modulo(first, second, &result, scale)) {
402422
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
403-
goto cleanup;
423+
goto fail;
404424
}
405425

426+
bc_free_num(&first);
427+
bc_free_num(&second);
406428
RETVAL_STR(bc_num2str_ex(result, scale));
407429

408-
cleanup: {
430+
bc_free_num(&result);
431+
return;
432+
433+
fail: {
409434
bc_free_num(&first);
410435
bc_free_num(&second);
411436
bc_free_num(&result);
@@ -446,43 +471,47 @@ PHP_FUNCTION(bcpowmod)
446471

447472
if (php_str2num(&bc_base, ZSTR_VAL(base_str)) == FAILURE) {
448473
zend_argument_value_error(1, "is not well-formed");
449-
goto cleanup;
474+
goto fail;
450475
}
451476

452477
if (php_str2num(&bc_expo, ZSTR_VAL(exponent_str)) == FAILURE) {
453478
zend_argument_value_error(2, "is not well-formed");
454-
goto cleanup;
479+
goto fail;
455480
}
456481

457482
if (php_str2num(&bc_modulus, ZSTR_VAL(modulus_str)) == FAILURE) {
458483
zend_argument_value_error(3, "is not well-formed");
459-
goto cleanup;
484+
goto fail;
460485
}
461486

462487
raise_mod_status status = bc_raisemod(bc_base, bc_expo, bc_modulus, &result, scale);
463488
switch (status) {
464489
case BASE_HAS_FRACTIONAL:
465490
zend_argument_value_error(1, "cannot have a fractional part");
466-
goto cleanup;
491+
goto fail;
467492
case EXPO_HAS_FRACTIONAL:
468493
zend_argument_value_error(2, "cannot have a fractional part");
469-
goto cleanup;
494+
goto fail;
470495
case EXPO_IS_NEGATIVE:
471496
zend_argument_value_error(2, "must be greater than or equal to 0");
472-
goto cleanup;
497+
goto fail;
473498
case MOD_HAS_FRACTIONAL:
474499
zend_argument_value_error(3, "cannot have a fractional part");
475-
goto cleanup;
500+
goto fail;
476501
case MOD_IS_ZERO:
477502
zend_throw_exception_ex(zend_ce_division_by_zero_error, 0, "Modulo by zero");
478-
goto cleanup;
503+
goto fail;
479504
case OK:
505+
bc_free_num(&bc_base);
506+
bc_free_num(&bc_expo);
507+
bc_free_num(&bc_modulus);
480508
RETVAL_STR(bc_num2str_ex(result, scale));
481-
break;
509+
bc_free_num(&result);
510+
return;
482511
EMPTY_SWITCH_DEFAULT_CASE();
483512
}
484513

485-
cleanup: {
514+
fail: {
486515
bc_free_num(&bc_base);
487516
bc_free_num(&bc_expo);
488517
bc_free_num(&bc_modulus);
@@ -522,30 +551,35 @@ PHP_FUNCTION(bcpow)
522551

523552
if (php_str2num(&first, ZSTR_VAL(base_str)) == FAILURE) {
524553
zend_argument_value_error(1, "is not well-formed");
525-
goto cleanup;
554+
goto fail;
526555
}
527556

528557
if (php_str2num(&bc_exponent, ZSTR_VAL(exponent_str)) == FAILURE) {
529558
zend_argument_value_error(2, "is not well-formed");
530-
goto cleanup;
559+
goto fail;
531560
}
532561

533562
/* Check the exponent for scale digits and convert to a long. */
534563
if (bc_exponent->n_scale != 0) {
535564
zend_argument_value_error(2, "cannot have a fractional part");
536-
goto cleanup;
565+
goto fail;
537566
}
538567
long exponent = bc_num2long(bc_exponent);
539568
if (exponent == 0 && (bc_exponent->n_len > 1 || bc_exponent->n_value[0] != 0)) {
540569
zend_argument_value_error(2, "is too large");
541-
goto cleanup;
570+
goto fail;
542571
}
543572

544573
bc_raise(first, exponent, &result, scale);
545574

575+
bc_free_num(&first);
576+
bc_free_num(&bc_exponent);
546577
RETVAL_STR(bc_num2str_ex(result, scale));
547578

548-
cleanup: {
579+
bc_free_num(&result);
580+
return;
581+
582+
fail: {
549583
bc_free_num(&first);
550584
bc_free_num(&bc_exponent);
551585
bc_free_num(&result);

ext/bcmath/tests/gh17398.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
GH-17398 (bcmul memory leak)
3+
--EXTENSIONS--
4+
bcmath
5+
--FILE--
6+
<?php
7+
bcmul('0', '0', 2147483647);
8+
?>
9+
--EXPECTF--
10+
Fatal error: Allowed memory size of %d bytes exhausted%s(tried to allocate %d bytes) in %s on line %d

0 commit comments

Comments
 (0)