aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSverker Eriksson <[email protected]>2016-02-12 18:52:20 +0100
committerLukas Larsson <[email protected]>2016-03-29 14:57:11 +0200
commita2a86dadc648dda68b5221a7c1d83b9238be1e25 (patch)
tree6229f416cc8a8e8a4b413a3a91246e37a693ff3a
parent209c5cf22b5cdc70eb48e6afdcddfa7132471aab (diff)
downloadotp-a2a86dadc648dda68b5221a7c1d83b9238be1e25.tar.gz
otp-a2a86dadc648dda68b5221a7c1d83b9238be1e25.tar.bz2
otp-a2a86dadc648dda68b5221a7c1d83b9238be1e25.zip
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
-rw-r--r--erts/doc/src/erl_nif.xml37
-rw-r--r--erts/emulator/beam/beam_load.c8
-rw-r--r--erts/emulator/beam/erl_nif.c41
-rw-r--r--erts/emulator/beam/erl_nif.h4
-rw-r--r--erts/emulator/beam/erl_nif_api_funcs.h2
-rw-r--r--erts/emulator/beam/external.c12
-rw-r--r--erts/emulator/beam/external.h2
-rw-r--r--erts/emulator/beam/io.c4
-rw-r--r--erts/emulator/test/nif_SUITE.erl17
-rw-r--r--erts/emulator/test/nif_SUITE_data/nif_SUITE.c20
10 files changed, 97 insertions, 50 deletions
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 {
<p>Note that <c>ErlNifBinary</c> is a semi-opaque type and you are
only allowed to read fields <c>size</c> and <c>data</c>.</p>
</item>
+
+ <tag><marker id="ErlNifBinaryToTerm"/>ErlNifBinaryToTerm</tag>
+ <item>
+ <p>An enumeration of the options that can be given to
+ <seealso marker="#enif_binary_to_term">enif_binary_to_term</seealso>.
+ For default behavior, use the value <c>0</c>.</p>
+ <taglist>
+ <tag><c>ERL_NIF_BIN2TERM_SAFE</c></tag>
+ <item><p>Use this option when receiving data from untrusted sources.</p></item>
+ </taglist>
+ </item>
+
<tag><marker id="ErlNifPid"/>ErlNifPid</tag>
<item>
<p><c>ErlNifPid</c> is a process identifier (pid). In contrast to
@@ -655,16 +667,23 @@ typedef enum {
have been allocated with <seealso marker="#enif_alloc_env">enif_alloc_env</seealso>.
</p></desc>
</func>
- <func><name><ret>int</ret><nametext>enif_binary_to_term(ErlNifEnv *env, ErlNifBinary *bin, ERL_NIF_TERM *term)</nametext></name>
- <fsummary>Convert a term from the external format</fsummary>
+ <func><name><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env, const unsigned char* data, size_t size, ERL_NIF_TERM *term, ErlNifBinaryToTerm opts)</nametext></name>
+ <fsummary>Create a term from the external format</fsummary>
<desc>
- <p>Returns a term that is the result of decoding <c>bin</c>
- according to the Erlang external term format.</p>
- <p>See also:</p>
- <list>
- <item><seealso marker="erlang#binary_to_term-1"><c>erlang:binary_to_term/1</c></seealso></item>
- <item><seealso marker="#enif_term_to_binary"><c>enif_term_to_binary</c></seealso></item>
- </list>
+ <p>Create a term that is the result of decoding the binary data
+ at <c>data</c>, which must be encoded according to the Erlang external term format.
+ No more than <c>size</c> bytes are read from <c>data</c>. Argument <c>opts</c>
+ correspond to the second argument to <seealso marker="erlang#binary_to_term-2">
+ <c>erlang:binary_to_term/2</c></seealso>, and must be either <c>0</c> or
+ <c>ERL_NIF_BIN2TERM_SAFE</c>.</p>
+ <p>On success, store the resulting term at <c>*term</c> and return
+ the actual number of bytes read. Return zero if decoding fails or if <c>opts</c>
+ is invalid.</p>
+ <p>See also:
+ <seealso marker="#ErlNifBinaryToTerm"><c>ErlNifBinaryToTerm</c></seealso>,
+ <seealso marker="erlang#binary_to_term-2"><c>erlang:binary_to_term/2</c></seealso> and
+ <seealso marker="#enif_term_to_binary"><c>enif_term_to_binary</c></seealso>.
+ </p>
</desc>
</func>
<func><name><ret>int</ret><nametext>enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext></name>
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}
};