Merge pull request #9279 from lafirest/fix/binary_to_atom

fix: use binary_to_existing_atom to replace some risky binary_to_atom
This commit is contained in:
lafirest 2022-11-02 10:57:05 +08:00 committed by GitHub
commit 5886db08e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 110 additions and 45 deletions

View File

@ -52,7 +52,9 @@
explain_posix/1, explain_posix/1,
pmap/2, pmap/2,
pmap/3, pmap/3,
readable_error_msg/1 readable_error_msg/1,
safe_to_existing_atom/1,
safe_to_existing_atom/2
]). ]).
-export([ -export([
@ -463,6 +465,18 @@ nolink_apply(Fun, Timeout) when is_function(Fun, 0) ->
exit(timeout) exit(timeout)
end. end.
safe_to_existing_atom(In) ->
safe_to_existing_atom(In, utf8).
safe_to_existing_atom(Bin, Encoding) when is_binary(Bin) ->
try_to_existing_atom(fun erlang:binary_to_existing_atom/2, [Bin, Encoding]);
safe_to_existing_atom(List, _Encoding) when is_list(List) ->
try_to_existing_atom(fun erlang:list_to_existing_atom/1, [List]);
safe_to_existing_atom(Atom, _Encoding) when is_atom(Atom) ->
{ok, Atom};
safe_to_existing_atom(_Any, _Encoding) ->
{error, invalid_type}.
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Internal Functions %% Internal Functions
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
@ -533,6 +547,14 @@ readable_error_msg(Error) ->
end end
end. end.
try_to_existing_atom(Fun, Args) ->
try erlang:apply(Fun, Args) of
Atom ->
{ok, Atom}
catch
_:Reason -> {error, Reason}
end.
-ifdef(TEST). -ifdef(TEST).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").

View File

@ -492,23 +492,16 @@ lookup_from_local_node(BridgeType, BridgeName) ->
invalid -> invalid ->
{400, error_msg('BAD_REQUEST', <<"invalid operation">>)}; {400, error_msg('BAD_REQUEST', <<"invalid operation">>)};
OperFunc -> OperFunc ->
TargetNode = binary_to_atom(Node, utf8),
ConfMap = emqx:get_config([bridges, BridgeType, BridgeName]), ConfMap = emqx:get_config([bridges, BridgeType, BridgeName]),
case maps:get(enable, ConfMap, false) of case maps:get(enable, ConfMap, false) of
false -> false ->
{403, {403,
error_msg( error_msg(
'FORBIDDEN_REQUEST', <<"forbidden operation: bridge disabled">> 'FORBIDDEN_REQUEST',
<<"forbidden operation: bridge disabled">>
)}; )};
true -> true ->
case emqx_bridge_proto_v1:OperFunc(TargetNode, BridgeType, BridgeName) of call_operation(Node, OperFunc, BridgeType, BridgeName)
ok ->
{200};
{error, timeout} ->
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
{error, Reason} ->
{500, error_msg('INTERNAL_ERROR', Reason)}
end
end end
end end
). ).
@ -707,3 +700,22 @@ bin(S) when is_atom(S) ->
atom_to_binary(S, utf8); atom_to_binary(S, utf8);
bin(S) when is_binary(S) -> bin(S) when is_binary(S) ->
S. S.
call_operation(Node, OperFunc, BridgeType, BridgeName) ->
case emqx_misc:safe_to_existing_atom(Node, utf8) of
{ok, TargetNode} ->
case
emqx_bridge_proto_v1:OperFunc(
TargetNode, BridgeType, BridgeName
)
of
ok ->
{200};
{error, timeout} ->
{503, error_msg('SERVICE_UNAVAILABLE', <<"request timeout">>)};
{error, Reason} ->
{500, error_msg('INTERNAL_ERROR', Reason)}
end;
{error, _} ->
{400, error_msg('INVALID_NODE', <<"invalid node">>)}
end.

View File

@ -59,9 +59,10 @@ bridge_id(BridgeType, BridgeName) ->
Type = bin(BridgeType), Type = bin(BridgeType),
<<Type/binary, ":", Name/binary>>. <<Type/binary, ":", Name/binary>>.
-spec parse_bridge_id(list() | binary() | atom()) -> {atom(), binary()}.
parse_bridge_id(BridgeId) -> parse_bridge_id(BridgeId) ->
case string:split(bin(BridgeId), ":", all) of case string:split(bin(BridgeId), ":", all) of
[Type, Name] -> {binary_to_atom(Type, utf8), binary_to_atom(Name, utf8)}; [Type, Name] -> {binary_to_atom(Type, utf8), Name};
_ -> error({invalid_bridge_id, BridgeId}) _ -> error({invalid_bridge_id, BridgeId})
end. end.

View File

@ -78,9 +78,10 @@ connector_id(Type0, Name0) ->
Name = bin(Name0), Name = bin(Name0),
<<Type/binary, ":", Name/binary>>. <<Type/binary, ":", Name/binary>>.
-spec parse_connector_id(binary() | list() | atom()) -> {atom(), binary()}.
parse_connector_id(ConnectorId) -> parse_connector_id(ConnectorId) ->
case string:split(bin(ConnectorId), ":", all) of case string:split(bin(ConnectorId), ":", all) of
[Type, Name] -> {binary_to_atom(Type, utf8), binary_to_atom(Name, utf8)}; [Type, Name] -> {binary_to_atom(Type, utf8), Name};
_ -> error({invalid_connector_id, ConnectorId}) _ -> error({invalid_connector_id, ConnectorId})
end. end.

View File

@ -131,12 +131,20 @@ monitor(get, #{query_string := QS, bindings := Bindings}) ->
end. end.
monitor_current(get, #{bindings := Bindings}) -> monitor_current(get, #{bindings := Bindings}) ->
NodeOrCluster = binary_to_atom(maps:get(node, Bindings, <<"all">>), utf8), RawNode = maps:get(node, Bindings, all),
case emqx_dashboard_monitor:current_rate(NodeOrCluster) of case emqx_misc:safe_to_existing_atom(RawNode, utf8) of
{ok, CurrentRate} -> {ok, NodeOrCluster} ->
{200, CurrentRate}; case emqx_dashboard_monitor:current_rate(NodeOrCluster) of
{badrpc, {Node, Reason}} -> {ok, CurrentRate} ->
Message = list_to_binary(io_lib:format("Bad node ~p, rpc failed ~p", [Node, Reason])), {200, CurrentRate};
{badrpc, {Node, Reason}} ->
Message = list_to_binary(
io_lib:format("Bad node ~p, rpc failed ~p", [Node, Reason])
),
{400, 'BAD_RPC', Message}
end;
{error, _} ->
Message = list_to_binary(io_lib:format("Bad node ~p", [RawNode])),
{400, 'BAD_RPC', Message} {400, 'BAD_RPC', Message}
end. end.

View File

@ -121,7 +121,13 @@ apply_publish_opts(Msg, MQTTMsg) ->
maps:fold( maps:fold(
fun fun
(<<"retain">>, V, Acc) -> (<<"retain">>, V, Acc) ->
Val = erlang:binary_to_atom(V), Val =
case emqx_misc:safe_to_existing_atom(V) of
{ok, true} ->
true;
_ ->
false
end,
emqx_message:set_flag(retain, Val, Acc); emqx_message:set_flag(retain, Val, Acc);
(<<"expiry">>, V, Acc) -> (<<"expiry">>, V, Acc) ->
Val = erlang:binary_to_integer(V), Val = erlang:binary_to_integer(V),

View File

@ -115,15 +115,19 @@ clients(get, #{
?QUERY_FUN ?QUERY_FUN
); );
Node0 -> Node0 ->
Node1 = binary_to_atom(Node0, utf8), case emqx_misc:safe_to_existing_atom(Node0) of
QStringWithoutNode = maps:without([<<"node">>], QString), {ok, Node1} ->
emqx_mgmt_api:node_query( QStringWithoutNode = maps:without([<<"node">>], QString),
Node1, emqx_mgmt_api:node_query(
QStringWithoutNode, Node1,
TabName, QStringWithoutNode,
?CLIENT_QSCHEMA, TabName,
?QUERY_FUN ?CLIENT_QSCHEMA,
) ?QUERY_FUN
);
{error, _} ->
{error, Node0, {badrpc, <<"invalid node">>}}
end
end, end,
case Result of case Result of
{error, page_limit_invalid} -> {error, page_limit_invalid} ->

View File

@ -648,15 +648,19 @@ list_clients(QString) ->
?QUERY_FUN ?QUERY_FUN
); );
Node0 -> Node0 ->
Node1 = binary_to_atom(Node0, utf8), case emqx_misc:safe_to_existing_atom(Node0) of
QStringWithoutNode = maps:without([<<"node">>], QString), {ok, Node1} ->
emqx_mgmt_api:node_query( QStringWithoutNode = maps:without([<<"node">>], QString),
Node1, emqx_mgmt_api:node_query(
QStringWithoutNode, Node1,
?CLIENT_QTAB, QStringWithoutNode,
?CLIENT_QSCHEMA, ?CLIENT_QTAB,
?QUERY_FUN ?CLIENT_QSCHEMA,
) ?QUERY_FUN
);
{error, _} ->
{error, Node0, {badrpc, <<"invalid node">>}}
end
end, end,
case Result of case Result of
{error, page_limit_invalid} -> {error, page_limit_invalid} ->

View File

@ -145,13 +145,18 @@ subscriptions(get, #{query_string := QString}) ->
?QUERY_FUN ?QUERY_FUN
); );
Node0 -> Node0 ->
emqx_mgmt_api:node_query( case emqx_misc:safe_to_existing_atom(Node0) of
binary_to_atom(Node0, utf8), {ok, Node1} ->
QString, emqx_mgmt_api:node_query(
?SUBS_QTABLE, Node1,
?SUBS_QSCHEMA, QString,
?QUERY_FUN ?SUBS_QTABLE,
) ?SUBS_QSCHEMA,
?QUERY_FUN
);
{error, _} ->
{error, Node0, {badrpc, <<"invalid node">>}}
end
end, end,
case Response of case Response of
{error, page_limit_invalid} -> {error, page_limit_invalid} ->

View File

@ -15,6 +15,7 @@
- Now it is possible to opt out VM internal metrics in prometheus stats [#9222](https://github.com/emqx/emqx/pull/9222). - Now it is possible to opt out VM internal metrics in prometheus stats [#9222](https://github.com/emqx/emqx/pull/9222).
When system load is high, reporting too much metrics data may cause the prometheus stats API timeout. When system load is high, reporting too much metrics data may cause the prometheus stats API timeout.
- Improve security when converting types such as `binary` `lists` to `atom` types [#9279](https://github.com/emqx/emqx/pull/9279).
## Bug fixes ## Bug fixes

View File

@ -14,6 +14,7 @@
- 可通过配置关闭 prometheus 中的部分内部指标,如果遇到机器负载过高 prometheus 接口返回超时可考虑关闭部分不关心指标,以提高响应速度 [#9222](https://github.com/emqx/emqx/pull/9222 - 可通过配置关闭 prometheus 中的部分内部指标,如果遇到机器负载过高 prometheus 接口返回超时可考虑关闭部分不关心指标,以提高响应速度 [#9222](https://github.com/emqx/emqx/pull/9222
- 提升 `binary` 、`list` 等类型转换为 `atom` 类型时的安全性 [#9279](https://github.com/emqx/emqx/pull/9279)。
## Bug fixes ## Bug fixes