Skip to content

Commit b7745b4

Browse files
committed
interop: fix downcast
Code in folding functions casted avm_int_t to char, silently truncating input. Now we check if the value fits in the char and fail operation if not. Signed-off-by: Jakub Gonet <jakub.gonet@swmansion.com>
1 parent a9ade6c commit b7745b4

File tree

2 files changed

+62
-52
lines changed

2 files changed

+62
-52
lines changed

src/libAtomVM/interop.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "tempstack.h"
2727
#include "term.h"
2828
#include "term_typedef.h"
29+
#include "utils.h"
2930
#include "valueshashtable.h"
3031
#include <stdint.h>
3132

@@ -307,10 +308,19 @@ inline InteropFunctionResult interop_chardata_fold(term t, interop_chardata_fold
307308
return InteropOk;
308309
}
309310

311+
static inline bool is_u8(avm_int_t value)
312+
{
313+
return 0 <= value && value <= UCHAR_MAX;
314+
}
315+
310316
static inline InteropFunctionResult size_fold_fun(term t, void *accum)
311317
{
312318
size_t *size = (size_t *) accum;
313319
if (term_is_integer(t)) {
320+
avm_int_t value = term_to_int(t);
321+
if (UNLIKELY(!is_u8(value))) {
322+
return InteropBadArg;
323+
}
314324
*size += 1;
315325
} else /* term_is_binary(t) */ {
316326
*size += term_binary_size(t);
@@ -328,9 +338,15 @@ static inline InteropFunctionResult write_string_fold_fun(term t, void *accum)
328338
{
329339
char **p = (char **) accum;
330340
if (term_is_integer(t)) {
331-
**p = term_to_int(t);
341+
avm_int_t value = term_to_int(t);
342+
if (UNLIKELY(!is_u8(value))) {
343+
return InteropBadArg;
344+
}
345+
**p = value;
332346
(*p)++;
333-
} else /* term_is_binary(t) */ {
347+
} else {
348+
assert(term_is_binary(t));
349+
334350
int len = term_binary_size(t);
335351
memcpy(*p, term_binary_data(t), len);
336352
*p += len;

tests/erlang_tests/test_list_to_binary.erl

Lines changed: 44 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,77 +20,71 @@
2020

2121
-module(test_list_to_binary).
2222

23-
-export([start/0, concat/2, concat2/2, compare_bin/3, id/1]).
23+
-export([start/0, concat_space/2, concat/2, is_binary_equal/3, id/1]).
24+
-define(ID(X), ?MODULE:id(X)).
25+
-define(ID(X1, X2), ?MODULE:id(X1), ?MODULE:id(X2)).
2426

2527
start() ->
26-
ok = test_concat(),
27-
ok = test_iolist(),
28+
ok = test_list_to_binary(),
2829
ok = test_iolist_to_binary(),
29-
ok = test_empty_list_to_binary(),
3030
0.
31+
test_list_to_binary() ->
32+
<<"Hello world">> = erlang:list_to_binary(?ID([<<"Hello ">>, [<<"wor">>, [$l, $d]]])),
33+
<<"">> = erlang:list_to_binary(?ID([])),
3134

32-
test_concat() ->
33-
Bin = concat("Hello", "world"),
34-
Bin2 = concat2("", ""),
35-
CompRes1 = compare_bin(Bin, <<"Hello world">>) - compare_bin(Bin, <<"HelloXworld">>),
36-
1 = CompRes1 + byte_size(Bin2) + invalid(42),
37-
ok.
35+
% Concatenation
36+
Hello = concat_space(?ID("Hello", "world")),
37+
Empty = concat(?ID("", "")),
38+
0 = byte_size(Empty),
39+
true = is_binary_equal(Hello, <<"Hello world">>),
40+
false = is_binary_equal(Hello, <<"HelloXworld">>),
3841

39-
test_iolist() ->
40-
<<"Hello world">> = list_to_binary(id([<<"Hello ">>, [<<"wor">>, [$l, $d]]])),
42+
% Errors
43+
ok = raises_badarg(fun() -> erlang:list_to_binary(?ID(<<>>)) end),
44+
ok = raises_badarg(fun() -> erlang:list_to_binary(?ID(<<"foo">>)) end),
45+
ok = raises_badarg(fun() -> erlang:list_to_binary(?ID(42)) end),
4146
ok.
4247

4348
test_iolist_to_binary() ->
44-
<<"Hello world">> = iolist_to_binary(id([<<"Hello ">>, [<<"wor">>, [$l, $d]]])),
45-
ok =
46-
try
47-
_ = list_to_binary(id(<<"foo">>)),
48-
fail
49-
catch
50-
error:badarg ->
51-
ok
52-
end,
53-
ok =
54-
try
55-
<<"foo">> = iolist_to_binary(id(<<"foo">>)),
56-
ok
57-
catch
58-
error:badarg ->
59-
fail
60-
end,
61-
ok.
62-
63-
test_empty_list_to_binary() ->
64-
<<"">> = erlang:list_to_binary([]),
49+
<<"foo">> = erlang:iolist_to_binary(?ID(<<"foo">>)),
50+
<<"">> = erlang:iolist_to_binary(?ID(<<>>)),
51+
<<"">> = erlang:iolist_to_binary(?ID([])),
52+
% The binary and charlist is "atom" in katakana as english transliteration ("atomu")
53+
% (in direct binary form since formatter butchers the utf-8)
54+
<<227, 130, 162, 227, 131, 136, 227, 131, 160>> = iolist_to_binary(
55+
?ID(<<227, 130, 162, 227, 131, 136, 227, 131, 160>>)
56+
),
57+
ok = raises_badarg(fun() -> iolist_to_binary(?ID([12450, 12488, 12512])) end),
58+
ok = raises_badarg(fun() -> iolist_size(?ID([12450, 12488, 12512])) end),
6559
ok.
6660

67-
concat(A, B) ->
61+
concat_space(A, B) ->
6862
list_to_binary(A ++ " " ++ B).
6963

70-
concat2(A, B) ->
64+
concat(A, B) ->
7165
list_to_binary(A ++ B).
7266

73-
invalid(A) ->
74-
try list_to_binary(A) of
75-
Any -> byte_size(Any)
76-
catch
77-
error:badarg -> 0;
78-
_:_ -> 1000
79-
end.
80-
81-
compare_bin(Bin1, Bin2) ->
82-
compare_bin(Bin1, Bin2, byte_size(Bin1) - 1).
67+
is_binary_equal(Bin1, Bin2) ->
68+
is_binary_equal(Bin1, Bin2, byte_size(Bin1) - 1).
8369

84-
compare_bin(_Bin1, _Bin2, -1) ->
85-
1;
86-
compare_bin(Bin1, Bin2, Index) ->
70+
is_binary_equal(_Bin1, _Bin2, -1) ->
71+
true;
72+
is_binary_equal(Bin1, Bin2, Index) ->
8773
B1 = binary:at(Bin1, Index),
8874
case binary:at(Bin2, Index) of
8975
B1 ->
90-
compare_bin(Bin1, Bin2, Index - 1);
76+
is_binary_equal(Bin1, Bin2, Index - 1);
9177
_Any ->
92-
0
78+
false
9379
end.
9480

9581
id(X) ->
9682
X.
83+
84+
raises_badarg(Fun) ->
85+
try Fun() of
86+
Ret -> {unexpected, Ret}
87+
catch
88+
error:badarg -> ok;
89+
C:E -> {unexpected, C, E}
90+
end.

0 commit comments

Comments
 (0)