From a2a86dadc648dda68b5221a7c1d83b9238be1e25 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 12 Feb 2016 18:52:20 +0100 Subject: erts: Improve enif_binary_to_term * Accept a raw data buffer instead of ErlNifBinary * Accept option ERL_NIF_BIN2TERM_SAFE * Return number of read bytes --- erts/doc/src/erl_nif.xml | 37 ++++++++++++++++++------ erts/emulator/beam/beam_load.c | 8 +++--- erts/emulator/beam/erl_nif.c | 41 ++++++++++++++------------- erts/emulator/beam/erl_nif.h | 4 +++ erts/emulator/beam/erl_nif_api_funcs.h | 2 +- erts/emulator/beam/external.c | 12 ++++++-- erts/emulator/beam/external.h | 2 +- erts/emulator/beam/io.c | 4 +-- erts/emulator/test/nif_SUITE.erl | 17 ++++++++--- erts/emulator/test/nif_SUITE_data/nif_SUITE.c | 20 ++++++++----- 10 files changed, 97 insertions(+), 50 deletions(-) (limited to 'erts') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 81b6eed24a..7a8325c200 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -524,6 +524,18 @@ typedef struct {

Note that ErlNifBinary is a semi-opaque type and you are only allowed to read fields size and data.

+ + ErlNifBinaryToTerm + +

An enumeration of the options that can be given to + enif_binary_to_term. + For default behavior, use the value 0.

+ + ERL_NIF_BIN2TERM_SAFE +

Use this option when receiving data from untrusted sources.

+
+
+ ErlNifPid

ErlNifPid is a process identifier (pid). In contrast to @@ -655,16 +667,23 @@ typedef enum { have been allocated with enif_alloc_env.

- intenif_binary_to_term(ErlNifEnv *env, ErlNifBinary *bin, ERL_NIF_TERM *term) - Convert a term from the external format + size_tenif_binary_to_term(ErlNifEnv *env, const unsigned char* data, size_t size, ERL_NIF_TERM *term, ErlNifBinaryToTerm opts) + Create a term from the external format -

Returns a term that is the result of decoding bin - according to the Erlang external term format.

-

See also:

- - erlang:binary_to_term/1 - enif_term_to_binary - +

Create a term that is the result of decoding the binary data + at data, which must be encoded according to the Erlang external term format. + No more than size bytes are read from data. Argument opts + correspond to the second argument to + erlang:binary_to_term/2, and must be either 0 or + ERL_NIF_BIN2TERM_SAFE.

+

On success, store the resulting term at *term and return + the actual number of bytes read. Return zero if decoding fails or if opts + is invalid.

+

See also: + ErlNifBinaryToTerm, + erlang:binary_to_term/2 and + enif_term_to_binary. +

intenif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs) diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index f115df935f..d3d278fb81 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -1548,7 +1548,7 @@ read_literal_table(LoaderState* stp) erts_factory_heap_frag_init(&factory, new_literal_fragment(heap_size)); factory.alloc_type = ERTS_ALC_T_PREPARED_CODE; - val = erts_decode_ext(&factory, &p); + val = erts_decode_ext(&factory, &p, 0); if (is_non_value(val)) { LoadError1(stp, "literal %d: bad external format", i); @@ -1559,7 +1559,7 @@ read_literal_table(LoaderState* stp) } else { erts_factory_dummy_init(&factory); - val = erts_decode_ext(&factory, &p); + val = erts_decode_ext(&factory, &p, 0); if (is_non_value(val)) { LoadError1(stp, "literal %d: bad external format", i); } @@ -5719,7 +5719,7 @@ attributes_for_module(Process* p, /* Process whose heap to use. */ if (ext != NULL) { ErtsHeapFactory factory; erts_factory_proc_prealloc_init(&factory, p, code_hdr->attr_size_on_heap); - result = erts_decode_ext(&factory, &ext); + result = erts_decode_ext(&factory, &ext, 0); if (is_value(result)) { erts_factory_close(&factory); } @@ -5742,7 +5742,7 @@ compilation_info_for_module(Process* p, /* Process whose heap to use. */ if (ext != NULL) { ErtsHeapFactory factory; erts_factory_proc_prealloc_init(&factory, p, code_hdr->compile_size_on_heap); - result = erts_decode_ext(&factory, &ext); + result = erts_decode_ext(&factory, &ext, 0); if (is_value(result)) { erts_factory_close(&factory); } diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 6ed89780b4..d8f2272c6d 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -686,17 +686,25 @@ int enif_term_to_binary(ErlNifEnv *dst_env, ERL_NIF_TERM term, return 1; } -int enif_binary_to_term(ErlNifEnv *dst_env, ErlNifBinary *bin, - ERL_NIF_TERM *term) +size_t enif_binary_to_term(ErlNifEnv *dst_env, + const unsigned char* data, + size_t data_sz, + ERL_NIF_TERM *term, + ErlNifBinaryToTerm opts) { Sint size; ErtsHeapFactory factory; + byte *bp = (byte*) data; - if ((size = erts_decode_ext_size(bin->data, bin->size)) < 0) + ERTS_CT_ASSERT(ERL_NIF_BIN2TERM_SAFE == ERTS_DIST_EXT_BTT_SAFE); + + if (opts & ~ERL_NIF_BIN2TERM_SAFE) { + return 0; + } + if ((size = erts_decode_ext_size(bp, data_sz)) < 0) return 0; if (size > 0) { - byte *bp; if (is_internal_pid(dst_env->proc->common.id)) { flush_env(dst_env); @@ -710,25 +718,18 @@ int enif_binary_to_term(ErlNifEnv *dst_env, ErlNifBinary *bin, erts_factory_heap_frag_init(&factory, dst_env->heap_frag); } + } else { + erts_factory_dummy_init(&factory); + } - bp = bin->data; - *term = erts_decode_ext(&factory, &bp); - - if (is_non_value(*term)) { - return 0; - } + *term = erts_decode_ext(&factory, &bp, (Uint32)opts); - erts_factory_close(&factory); - } - else { - erts_factory_dummy_init(&factory); - *term = erts_decode_ext(&factory, &bin->data); - if (is_non_value(*term)) { - return 0; - } - ASSERT(is_immed(*term)); + if (is_non_value(*term)) { + return 0; } - return 1; + erts_factory_close(&factory); + ASSERT(bp > data); + return bp - data; } int enif_is_identical(Eterm lhs, Eterm rhs) diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 6ee22a693c..bc1f59bc90 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -207,6 +207,10 @@ typedef enum { ERL_NIF_UNIQUE_MONOTONIC = (1 << 1) } ErlNifUniqueInteger; +typedef enum { + ERL_NIF_BIN2TERM_SAFE = 0x20000000 +} ErlNifBinaryToTerm; + #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 32afb53bfc..35058afe7c 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -170,7 +170,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_is_process_alive, (ErlNifEnv *env, ErlNifPid *pi ERL_NIF_API_FUNC_DECL(int, enif_is_port_alive, (ErlNifEnv *env, ErlNifPort *port_id)); ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, ErlNifPort* port_id)); ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)); -ERL_NIF_API_FUNC_DECL(int, enif_binary_to_term, (ErlNifEnv *env, ErlNifBinary *bin, ERL_NIF_TERM *term)); +ERL_NIF_API_FUNC_DECL(size_t, enif_binary_to_term, (ErlNifEnv *env, const unsigned char* data, size_t sz, ERL_NIF_TERM *term, unsigned int opts)); ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)); /* diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index aac1490f0c..f5eb13421d 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -967,15 +967,23 @@ erts_decode_dist_ext(ErtsHeapFactory* factory, return THE_NON_VALUE; } -Eterm erts_decode_ext(ErtsHeapFactory* factory, byte **ext) +Eterm erts_decode_ext(ErtsHeapFactory* factory, byte **ext, Uint32 flags) { + ErtsDistExternal ede, *edep; Eterm obj; byte *ep = *ext; if (*ep++ != VERSION_MAGIC) { erts_factory_undo(factory); return THE_NON_VALUE; } - ep = dec_term(NULL, factory, ep, &obj, NULL); + if (flags) { + ASSERT(flags == ERTS_DIST_EXT_BTT_SAFE); + ede.flags = flags; /* a dummy struct just for the flags */ + edep = &ede; + } else { + edep = NULL; + } + ep = dec_term(edep, factory, ep, &obj, NULL); if (!ep) { #ifdef DEBUG bin_write(ERTS_PRINT_STDERR,NULL,*ext,500); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index d12051c6b4..87eff2fe9f 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -191,7 +191,7 @@ Eterm erts_decode_dist_ext(ErtsHeapFactory* factory, ErtsDistExternal *); Sint erts_decode_ext_size(byte*, Uint); Sint erts_decode_ext_size_ets(byte*, Uint); -Eterm erts_decode_ext(ErtsHeapFactory*, byte**); +Eterm erts_decode_ext(ErtsHeapFactory*, byte**, Uint32 flags); Eterm erts_decode_ext_ets(ErtsHeapFactory*, byte*); Eterm erts_term_to_binary(Process* p, Eterm Term, int level, Uint flags); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 093334aab7..c7b21c0386 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -4570,7 +4570,7 @@ port_sig_call(Port *prt, (void) erts_factory_message_create(&factory, rp, &rp_locks, hsz); endp = (byte *) resp_bufp; - msg = erts_decode_ext(&factory, &endp); + msg = erts_decode_ext(&factory, &endp, 0); if (is_value(msg)) { hp = erts_produce_heap(&factory, 3, @@ -4689,7 +4689,7 @@ erts_port_call(Process* c_p, hsz += 3; erts_factory_proc_prealloc_init(&factory, c_p, hsz); endp = (byte *) resp_bufp; - term = erts_decode_ext(&factory, &endp); + term = erts_decode_ext(&factory, &endp, 0); if (term == THE_NON_VALUE) return ERTS_PORT_OP_BADARG; hp = erts_produce_heap(&factory,3,0); diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index bbd523b4ef..4dcfbf35ca 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1971,13 +1971,22 @@ nif_term_to_binary(Config) -> true = term_to_binary_nif(T, self()), receive Bin -> ok end. +-define(ERL_NIF_BIN2TERM_SAFE, 16#20000000). + nif_binary_to_term(Config) -> ensure_lib_loaded(Config), T = {#{ok => nok}, <<0:8096>>, lists:seq(1,100)}, Bin = term_to_binary(T), - T = binary_to_term_nif(Bin, undefined), - true = binary_to_term_nif(Bin, self()), - receive T -> ok end. + Len = byte_size(Bin), + {Len,T} = binary_to_term_nif(Bin, undefined, 0), + Len = binary_to_term_nif(Bin, self(), 0), + T = receive M -> M after 1000 -> timeout end, + + {Len, T} = binary_to_term_nif(Bin, undefined, ?ERL_NIF_BIN2TERM_SAFE), + false = binary_to_term_nif(<<131,100,0,14,"undefined_atom">>, + undefined, ?ERL_NIF_BIN2TERM_SAFE), + false = binary_to_term_nif(Bin, undefined, 1), + ok. nif_port_command(Config) -> ensure_lib_loaded(Config), @@ -2069,7 +2078,7 @@ unique_integer_nif(_) -> ?nif_stub. is_process_alive_nif(_) -> ?nif_stub. is_port_alive_nif(_) -> ?nif_stub. term_to_binary_nif(_, _) -> ?nif_stub. -binary_to_term_nif(_, _) -> ?nif_stub. +binary_to_term_nif(_, _, _) -> ?nif_stub. port_command_nif(_, _) -> ?nif_stub. %% maps diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index 317f440257..b3c6cc5ba3 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -2057,25 +2057,31 @@ static ERL_NIF_TERM term_to_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM static ERL_NIF_TERM binary_to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ErlNifBinary bin; - ERL_NIF_TERM term; + ERL_NIF_TERM term, ret_term; ErlNifPid pid; ErlNifEnv *msg_env = env; + unsigned int opts; + ErlNifUInt64 ret; if (enif_get_local_pid(env, argv[1], &pid)) msg_env = enif_alloc_env(); - if (!enif_inspect_binary(env, argv[0], &bin)) + if (!enif_inspect_binary(env, argv[0], &bin) + || !enif_get_uint(env, argv[2], &opts)) return enif_make_badarg(env); - if (!enif_binary_to_term(env, &bin, &term)) - return enif_make_badarg(env); + ret = enif_binary_to_term(msg_env, bin.data, bin.size, &term, + (ErlNifBinaryToTerm)opts); + if (!ret) + return atom_false; + ret_term = enif_make_uint64(env, ret); if (msg_env != env) { enif_send(env, &pid, msg_env, term); enif_free_env(msg_env); - return atom_true; + return ret_term; } else { - return term; + return enif_make_tuple2(env, ret_term, term); } } @@ -2170,7 +2176,7 @@ static ErlNifFunc nif_funcs[] = {"is_process_alive_nif", 1, is_process_alive}, {"is_port_alive_nif", 1, is_port_alive}, {"term_to_binary_nif", 2, term_to_binary}, - {"binary_to_term_nif", 2, binary_to_term}, + {"binary_to_term_nif", 3, binary_to_term}, {"port_command_nif", 2, port_command} }; -- cgit v1.2.3