Skip to content

Commit 47ef614

Browse files
committed
lists_to_integer() & friends: Don't crash for overlong lists
Let `list_to_integer/1` and `list_to_integer/2` raise a `system_limit` exception when the result doesn't fit in a bignum. Let `string:to_integer/1` return `{error,system_limit}` when the result doesn't fit.
1 parent 73cb4d4 commit 47ef614

File tree

5 files changed

+44
-21
lines changed

5 files changed

+44
-21
lines changed

erts/emulator/beam/bif.c

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3357,6 +3357,9 @@ BIF_RETTYPE string_list_to_integer_1(BIF_ALIST_1)
33573357
case LTI_NO_INTEGER:
33583358
hp = HAlloc(BIF_P,3);
33593359
BIF_RET(TUPLE2(hp, am_error, am_no_integer));
3360+
case LTI_SYSTEM_LIMIT:
3361+
hp = HAlloc(BIF_P,3);
3362+
BIF_RET(TUPLE2(hp, am_error, am_system_limit));
33603363
default:
33613364
hp = HAlloc(BIF_P,3);
33623365
BIF_RET(TUPLE2(hp, res, tail));
@@ -3365,25 +3368,27 @@ BIF_RETTYPE string_list_to_integer_1(BIF_ALIST_1)
33653368

33663369
BIF_RETTYPE list_to_integer_1(BIF_ALIST_1)
33673370
{
3368-
/* Using erts_list_to_integer is about twice as fast as using
3369-
erts_chars_to_integer because we do not have to copy the
3370-
entire list */
3371+
/* Using erts_list_to_integer() is about twice as fast as using
3372+
* erts_chars_to_integer() because we do not have to copy the
3373+
* entire list. */
33713374
Eterm res;
33723375
Eterm dummy;
33733376
/* must be a list */
3374-
if (erts_list_to_integer(BIF_P, BIF_ARG_1, 10,
3375-
&res, &dummy) != LTI_ALL_INTEGER) {
3376-
BIF_ERROR(BIF_P,BADARG);
3377+
switch (erts_list_to_integer(BIF_P, BIF_ARG_1, 10, &res, &dummy)) {
3378+
case LTI_ALL_INTEGER:
3379+
BIF_RET(res);
3380+
case LTI_SYSTEM_LIMIT:
3381+
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
3382+
default:
3383+
BIF_ERROR(BIF_P, BADARG);
33773384
}
3378-
BIF_RET(res);
33793385
}
33803386

33813387
BIF_RETTYPE list_to_integer_2(BIF_ALIST_2)
33823388
{
3383-
/* Bif implementation is about 50% faster than pure erlang,
3384-
and since we have erts_chars_to_integer now it is simpler
3385-
as well. This could be optimized further if we did not have to
3386-
copy the list to buf. */
3389+
/* The BIF implementation is about 50% faster than pure Erlang,
3390+
* and since we now have erts_list_to_integer() it is simpler as
3391+
* well. */
33873392
Sint i;
33883393
Eterm res, dummy;
33893394
int base;
@@ -3399,11 +3404,14 @@ BIF_RETTYPE list_to_integer_2(BIF_ALIST_2)
33993404
BIF_ERROR(BIF_P, BADARG);
34003405
}
34013406

3402-
if (erts_list_to_integer(BIF_P, BIF_ARG_1, base,
3403-
&res, &dummy) != LTI_ALL_INTEGER) {
3404-
BIF_ERROR(BIF_P,BADARG);
3407+
switch (erts_list_to_integer(BIF_P, BIF_ARG_1, base, &res, &dummy)) {
3408+
case LTI_ALL_INTEGER:
3409+
BIF_RET(res);
3410+
case LTI_SYSTEM_LIMIT:
3411+
BIF_ERROR(BIF_P, SYSTEM_LIMIT);
3412+
default:
3413+
BIF_ERROR(BIF_P, BADARG);
34053414
}
3406-
BIF_RET(res);
34073415
}
34083416

34093417
/**********************************************************************/

erts/emulator/beam/big.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3008,6 +3008,11 @@ LTI_result_t erts_list_to_integer(Process *BIF_P, Eterm orig_list,
30083008
m = (lg2+D_EXP-1)/D_EXP; /* number of digits */
30093009
m = BIG_NEED_SIZE(m); /* number of words + thing */
30103010

3011+
if (m > BIG_ARITY_MAX) {
3012+
error_res = LTI_SYSTEM_LIMIT;
3013+
goto error;
3014+
}
3015+
30113016
hp = HAlloc(BIF_P, m);
30123017
hp_end = hp + m;
30133018

erts/emulator/beam/big.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ typedef enum {
188188
LTI_BAD_STRUCTURE = 0,
189189
LTI_NO_INTEGER = 1,
190190
LTI_SOME_INTEGER = 2,
191-
LTI_ALL_INTEGER = 3
191+
LTI_ALL_INTEGER = 3,
192+
LTI_SYSTEM_LIMIT = 4,
192193
} LTI_result_t;
193194

194195
LTI_result_t erts_list_to_integer(Process *BIF_P, Eterm orig_list,

erts/emulator/test/list_bif_SUITE.erl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,21 @@ t_list_to_integer(Config) when is_list(Config) ->
5050
12373 = (catch list_to_integer("12373")),
5151
-12373 = (catch list_to_integer("-12373")),
5252
12373 = (catch list_to_integer("+12373")),
53-
{'EXIT',{badarg,_}} = ( catch list_to_integer(abc)),
53+
{'EXIT',{badarg,_}} = (catch list_to_integer(abc)),
5454
{'EXIT',{badarg,_}} = (catch list_to_integer("")),
5555
{12373281903728109372810937209817320981321,"ABC"} = string:to_integer("12373281903728109372810937209817320981321ABC"),
5656
{-12373281903728109372810937209817320981321,"ABC"} = string:to_integer("-12373281903728109372810937209817320981321ABC"),
5757
{12,[345]} = string:to_integer([$1,$2,345]),
58-
{error, badarg} = string:to_integer([$1,$2,a]),
58+
{error,badarg} = string:to_integer([$1,$2,a]),
5959
{error,no_integer} = string:to_integer([$A]),
6060
{error,badarg} = string:to_integer($A),
61+
62+
%% System limit.
63+
Digits = lists:duplicate(11_000_000, $9),
64+
{'EXIT',{system_limit,_}} = catch list_to_integer(Digits),
65+
{'EXIT',{system_limit,_}} = catch list_to_integer(Digits, 16),
66+
{error,system_limit} = string:to_integer(Digits),
67+
6168
ok.
6269

6370
%% Test hd/1 with correct and incorrect arguments.

erts/emulator/test/num_bif_SUITE.erl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -670,9 +670,11 @@ t_string_to_integer(Config) when is_list(Config) ->
670670
{"10",''} %Base 20
671671
]),
672672

673-
%% log2 calculation overflow bug in do_integer_to_list (OTP-12624).
674-
%% Would crash with segmentation fault.
675-
0 = list_to_integer(lists:duplicate(10000000,$0)),
673+
%% System limit
674+
Digits = lists:duplicate(11_000_000, $9),
675+
{'EXIT',{system_limit,_}} = catch list_to_integer(Digits),
676+
{'EXIT',{system_limit,_}} = catch list_to_integer(Digits, 16),
677+
{error,system_limit} = string:to_integer(Digits),
676678

677679
ok.
678680

0 commit comments

Comments
 (0)