diff options
author | Björn Gustavsson <bjorn@erlang.org> | 2022-03-02 13:45:36 +0100 |
---|---|---|
committer | Björn Gustavsson <bjorn@erlang.org> | 2022-03-02 13:45:36 +0100 |
commit | c4965867f8beac53a48dfcab056cf51f93cbaea6 (patch) | |
tree | 29d014f797e88d7fbd184c82677d89a3e522d7b3 /erts | |
parent | ac7e9cc992287e4c388be73f370ffcaada379fd5 (diff) | |
parent | ebcbb97b4ec223464cac3d94375739a248ddef6e (diff) | |
download | erlang-c4965867f8beac53a48dfcab056cf51f93cbaea6.tar.gz |
Merge branch 'bjorn/erts/binary_to_term/OTP-17595'
* bjorn/erts/binary_to_term/OTP-17595:
Clean up size calculation for binary_to_term/1
Diffstat (limited to 'erts')
-rw-r--r-- | erts/emulator/beam/external.c | 62 | ||||
-rw-r--r-- | erts/emulator/test/binary_SUITE.erl | 5 |
2 files changed, 46 insertions, 21 deletions
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 48c5c20bdd..8f68e155e7 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -5373,11 +5373,21 @@ static Sint decoded_size(const byte *ep, const byte* endp, int internal_tags, B2TContext* ctx) { Sint heap_size; - int terms; int atom_extra_skip; Uint n; SWord reds; + /* Keep track of the current number of sub terms remaining to be decoded. + * + * We limit the number of sub terms to 2^32-1, even on 64-bit + * machines, because a term that has many sub-terms must be truly + * ginormous and is proably a mistake. + * + * This means that a map with 2^31 or more elements cannot be decoded, + * even on a 64-bit machine. + */ + Uint32 terms; + if (ctx) { reds = ctx->reds; if (ctx->u.sc.ep) { @@ -5416,9 +5426,11 @@ init_done: if ((sz) > endp-ep) { goto error; } \ } while (0) +/* Increment the number of terms that remain to decode + * and check for the term counter wrapping around. */ #define ADDTERMS(n) \ do { \ - int before = terms; \ + Uint32 before = terms; \ terms += (n); \ if (terms < before) goto error; \ } while (0) @@ -5503,7 +5515,7 @@ init_done: case_PID: /* In case it is an external pid */ heap_size += EXTERNAL_PID_HEAP_SIZE; - terms++; + ADDTERMS(1); break; case V4_PORT_EXT: atom_extra_skip = 12; @@ -5516,7 +5528,7 @@ init_done: case_PORT: /* In case it is an external port */ heap_size += EXTERNAL_PORT_HEAP_SIZE; - terms++; + ADDTERMS(1); break; case NEWER_REFERENCE_EXT: atom_extra_skip = 4; @@ -5541,7 +5553,7 @@ init_done: #else heap_size += EXTERNAL_THING_HEAD_SIZE + id_words; #endif - terms++; + ADDTERMS(1); break; } case REFERENCE_EXT: @@ -5556,20 +5568,23 @@ init_done: CHKSIZE(4); n = get_uint32(ep); ep += 4; - ADDTERMS(n); - terms++; + CHKSIZE(n); /* Fail faster if the binary is too short. */ + /* Count terms in two operations to avoid overflow. */ + ADDTERMS(n); + ADDTERMS(1); heap_size += 2 * n; break; case SMALL_TUPLE_EXT: CHKSIZE(1); n = *ep++; - terms += n; + ADDTERMS(n); heap_size += n + 1; break; case LARGE_TUPLE_EXT: CHKSIZE(4); n = get_uint32(ep); ep += 4; + CHKSIZE(n); /* Fail faster if the binary is too short. */ ADDTERMS(n); heap_size += n + 1; break; @@ -5577,19 +5592,25 @@ init_done: CHKSIZE(4); n = get_uint32(ep); ep += 4; - ADDTERMS(2*n); if (n <= MAP_SMALL_MAP_LIMIT) { heap_size += 3 + n + 1 + n; - } else { -#if !defined(ARCH_64) - if ((n >> 30) != 0) { - /* Can't possibly fit in memory. */ - goto error; - } +#if defined(ARCH_64) + } else if ((n >> 31) != 0) { + /* Avoid overflow by limiting the number of elements in + * a map to 2^31-1 (about 2 billions). */ + goto error; +#else + } else if ((n >> 30) != 0) { + /* Can't possibly fit in memory on 32-bit machine. */ + goto error; #endif - CHKSIZE(2*n); /* Conservative size check */ + } else { + CHKSIZE(2*(Uint)n); /* Fail faster if the binary is too short. */ heap_size += HASHMAP_ESTIMATED_HEAP_SIZE(n); } + /* Count terms in two operations to avoid overflow. */ + ADDTERMS(n); + ADDTERMS(n); break; case STRING_EXT: CHKSIZE(2); @@ -5628,7 +5649,7 @@ init_done: } break; case EXPORT_EXT: - terms += 3; + ADDTERMS(3); heap_size += ERL_FUN_SIZE; break; case NEW_FUN_EXT: @@ -5646,7 +5667,7 @@ init_done: if (num_free > MAX_ARG) { goto error; } - terms += 4 + num_free; + ADDTERMS(4 + num_free); heap_size += ERL_FUN_SIZE + num_free; break; } @@ -5684,7 +5705,7 @@ init_done: } terms--; - if (ctx && --reds <= 0 && terms > 0) { + if (ctx && --reds <= 0 && terms != 0) { ctx->u.sc.heap_size = heap_size; ctx->u.sc.terms = terms; ctx->u.sc.ep = ep; @@ -5692,9 +5713,8 @@ init_done: ctx->reds = 0; return 0; } - }while (terms > 0); + } while (terms != 0); - /* 'terms' may be non-zero if it has wrapped around */ if (terms == 0) { if (ctx) { ctx->state = B2TDecodeInit; diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 58d4f83a2c..c23b937fc3 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1545,11 +1545,14 @@ test_terms(Test_Func) -> Test_Func("abcdef"), Test_Func([a, b, 1, 2]), Test_Func([a|b]), + Test_Func([make_port(), make_ref(), make_pid(), fun() -> ok end, + Very_Big | lists:seq(1, 75)]), Test_Func({}), Test_Func({1}), Test_Func({a, b}), Test_Func({a, b, c}), + Test_Func({make_port(), make_ref(), make_pid(), fun() -> ok end}), Test_Func(list_to_tuple(lists:seq(0, 255))), Test_Func(list_to_tuple(lists:seq(0, 256))), @@ -1600,9 +1603,11 @@ test_terms(Test_Func) -> Test_Func(<<42:10>>), Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])), + %% Funs in a list. Test_Func(F = fun(A) -> 42*A end), Test_Func(lists:duplicate(32, F)), + %% External funs in a list. Test_Func(FF = fun binary_SUITE:all/0), Test_Func(lists:duplicate(32, FF)), |