[topl] Update static matching for Erlang procedures.

Summary:
Topl matches procedure names against whatever Procname.hashable_name
returns. For Java, it returns something like "java.util.ArrayList.add(...)",
but for Erlang it used to return something like "add/1": no module and
no parenthesis after. Now, Erlang returns "lists:add/1"; that is, it
includes module name. Also, Topl doesn't insist anymore on having a
paranthesis after the name.

Differential Revision: D29520365

fbshipit-source-id: d23be1cc8
master
Radu Grigore 3 years ago committed by Facebook GitHub Bot
parent 0f4394503b
commit 2ecfa422ac

@ -152,6 +152,7 @@ ifeq ($(BUILD_ERLANG_ANALYZERS),yes)
ifneq ($(REBAR3),no) ifneq ($(REBAR3),no)
DIRECT_TESTS += \ DIRECT_TESTS += \
erlang_nonmatch \ erlang_nonmatch \
erlang_topl \
BUILD_SYSTEMS_TESTS += rebar3 BUILD_SYSTEMS_TESTS += rebar3
endif endif

@ -897,6 +897,8 @@ let replace_csharp_inner_class_prefix_regex = replace_regex csharp_inner_class_p
let hashable_name proc_name = let hashable_name proc_name =
match proc_name with match proc_name with
| Erlang pname ->
F.asprintf "%a" (Erlang.pp Verbose) pname
| Java pname -> | Java pname ->
(* Strip autogenerated anonymous inner class numbers in order to keep the bug hash (* Strip autogenerated anonymous inner class numbers in order to keep the bug hash
invariant when introducing new anonymous classes *) invariant when introducing new anonymous classes *)

@ -7,12 +7,21 @@
open! IStd open! IStd
let pp_raw_label f label = let pp_variable_name f name = Format.fprintf f "%s" name
match label.ToplAst.pattern with
let pp_pattern f (pattern : ToplAst.label_pattern) =
match pattern with
| ArrayWritePattern -> | ArrayWritePattern ->
Format.fprintf f "#ArrayWrite" Format.fprintf f "#ArrayWrite"
| ProcedureNamePattern procedure_name -> | ProcedureNamePattern procedure_name ->
Format.fprintf f "%s" procedure_name Format.fprintf f "%s" procedure_name
let pp_raw_label f {ToplAst.pattern; arguments; condition= _; action= _} =
(* TODO: Print condition and action. *)
Format.fprintf f "%a %a when CONDITION => ACTION" pp_pattern pattern
(Pp.option (Pp.comma_seq pp_variable_name))
arguments
let pp_label = Pp.option pp_raw_label let pp_label = Pp.option pp_raw_label

@ -81,8 +81,8 @@ let make properties =
let prefix_pname pname = let prefix_pname pname =
if String.equal ".*" pname then pname if String.equal ".*" pname then pname
else else
let ps = List.map ~f:(fun p -> "\\|" ^ p ^ "\\.") p.ToplAst.prefixes in let ps = List.map ~f:(fun p -> "\\|" ^ p ^ ".") p.ToplAst.prefixes in
"^\\(" ^ String.concat ps ^ "\\)" ^ pname ^ "(" "^\\(" ^ String.concat ps ^ "\\)" ^ pname ^ "\\((\\|$\\)"
in in
let prefix_pattern = let prefix_pattern =
ToplAst.( ToplAst.(

@ -0,0 +1,16 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
# see explanations in cpp/biabduction/Makefile for the custom isystem
INFER_OPTIONS = --topl --topl-properties taint.topl --project-root $(TESTS_DIR)
INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard src/*.erl)
include $(TESTS_DIR)/erlang.make
infer-out/report.json: $(MAKEFILE_LIST)

@ -0,0 +1,6 @@
codetoanalyze/erlang/topl/src/topl_taint.erl, fp_test_f_Bad/0, 1, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [no pattern match here]
codetoanalyze/erlang/topl/src/topl_taint.erl, fp_test_g_Ok/0, 1, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [no pattern match here]
codetoanalyze/erlang/topl/src/topl_taint.erl, test_a_Bad/0, 0, TOPL_ERROR, no_bucket, ERROR, [call to source/0,call to sink/1]
codetoanalyze/erlang/topl/src/topl_taint.erl, test_d_Bad/0, 0, TOPL_ERROR, no_bucket, ERROR, [call to source/0,call to call_sink_indirectly/1,call to call_sink/1,call to sink/1]
codetoanalyze/erlang/topl/src/topl_taint.erl, test_h_Bad/0, 0, TOPL_ERROR, no_bucket, ERROR, [call to dirty_if_argument_nil/1,call to source/0,call to sink/1]
codetoanalyze/erlang/topl/src/topl_taint.erl, test_j_Bad/0, 1, NONEXHAUSTIVE_PATTERN_MATCH, no_bucket, ERROR, [no pattern match here]

@ -0,0 +1,11 @@
% Copyright (c) Facebook, Inc. and its affiliates.
%
% This source code is licensed under the MIT license found in the
% LICENSE file in the root directory of this source tree.
{erl_opts, []}.
{deps, []}.
{shell, [
{apps, [topl_taint]}
]}.

@ -0,0 +1,14 @@
% Copyright (c) Facebook, Inc. and its affiliates.
%
% This source code is licensed under the MIT license found in the
% LICENSE file in the root directory of this source tree.
{application, topl_taint, [
{description, "An Erlang app that leaks"},
{vsn, "0.1.0"},
{registered, []},
{applications, [
kernel,
stdlib
]},
{modules, []}
]}.

@ -0,0 +1,84 @@
% Copyright (c) Facebook, Inc. and its affiliates.
%
% This source code is licensed under the MIT license found in the
% LICENSE file in the root directory of this source tree.
-module(topl_taint).
-export([
test_a_Bad/0,
test_b_Ok/0,
fn_test_c_Bad/0,
test_d_Bad/0,
test_e_Ok/0,
fp_test_f_Bad/0,
fp_test_g_Ok/0,
test_h_Bad/0,
test_i_Ok/0,
test_j_Bad/0
]).
test_a_Bad() ->
sink(source()).
test_b_Ok() ->
sink(1).
% FN because match not yet translated
fn_test_c_Bad() ->
X = source(),
sink(X).
test_d_Bad() ->
call_sink_indirectly(tito(source())).
test_e_Ok() ->
call_sink_indirectly(tito(ok)).
% FP: T94670024 (bad, but wrong warning reported)
fp_test_f_Bad() ->
case one() + one() - two() =:= 0 of
true -> sink(dirty_if_argument_nil([]));
false -> sink(dirty_if_argument_nil([1]))
end.
% FP: T94670024
fp_test_g_Ok() ->
case one() + one() - two() =/= 0 of
true -> sink(dirty_if_argument_nil([]));
false -> sink(dirty_if_argument_nil([1]))
end.
test_h_Bad() ->
case one() + one() of
1 -> sink(dirty_if_argument_nil([ok]));
2 -> sink(dirty_if_argument_nil([]))
end.
test_i_Ok() ->
case two() of
1 -> sink(dirty_if_argument_nil(bad));
2 -> sink(dirty_if_argument_nil([ok, ok, ok]))
end.
% Bad because no case for 3 (not a topl property)
test_j_Bad() ->
case two() + two() - one() of
1 -> sink(dirty_if_argument_nil(bad));
2 -> sink(dirty_if_argument_nil([ok, ok, ok]))
end.
%%
% tito = Taint-In Taint-Out
tito(X) -> X.
call_sink_indirectly(X) -> call_sink(X).
call_sink(X) -> sink(X).
dirty_if_argument_nil([]) -> source();
dirty_if_argument_nil([X | _]) -> X.
one() -> 1.
two() -> 2.
%%
source() -> dirty.
sink(_) -> ok.

@ -0,0 +1,5 @@
property Taint
prefix "topl_taint"
start -> start: *
start -> tracking: "source/0"(Ret) => x := Ret
tracking -> error: "sink/1"(Arg, VoidRet) when x == Arg
Loading…
Cancel
Save