Summary: Introduce a new experimental checker (`--impurity`) that detects impurity information, tracking which parameters and global variables of a function are modified. The checker relies on Pulse to detect how the state changes: it traverses the pre and post pairs starting from the parameter/global variable and finds where the pre and post heaps diverge. At diversion points, we expect to see WrittenTo/Invalid attributes containing a trace of how the address was modified. We use these to construct the trace of impurity. This checker is a complement to the purity checker that exists mainly for Java (and used for cost and loop-hoisting analyses). The aim of this new experimental checker is to rely on Pulse's precise memory treatment and come up with a more precise im(purity) analysis. To distinguish the two checkers, we introduce a new issue type `IMPURE_FUNCTION` that reports when a function is impure, rather than when it is pure (as in the purity checker). TODO: - improve the analysis to rely on impurity information of external library calls. Currently, all library calls are assumed to be nops, hence pure. - de-entangle Pulse reporting from analysis. Reviewed By: skcho Differential Revision: D17051567 fbshipit-source-id: 5e10afb4fmaster
parent
c19d9254b4
commit
c5ca4db8d0
@ -0,0 +1,153 @@
|
||||
(*
|
||||
* 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.
|
||||
*)
|
||||
open! IStd
|
||||
module F = Format
|
||||
module L = Logging
|
||||
module AbstractAddress = PulseDomain.AbstractAddress
|
||||
module Attributes = PulseDomain.Attributes
|
||||
module BaseStack = PulseDomain.Stack
|
||||
|
||||
let debug fmt = L.(debug Analysis Verbose fmt)
|
||||
|
||||
(* An impurity analysis that relies on pulse to determine how the state
|
||||
changes *)
|
||||
|
||||
module Payload = SummaryPayload.Make (struct
|
||||
type t = ImpurityDomain.t
|
||||
|
||||
let field = Payloads.Fields.impurity
|
||||
end)
|
||||
|
||||
let get_matching_dest_addr_opt ~edges_pre ~edges_post : AbstractAddress.t list option =
|
||||
match
|
||||
List.fold2 ~init:(Some [])
|
||||
~f:(fun acc (_, (addr_dest_pre, _)) (_, (addr_dest_post, _)) ->
|
||||
if AbstractAddress.equal addr_dest_pre addr_dest_post then
|
||||
Option.map acc ~f:(fun acc -> addr_dest_pre :: acc)
|
||||
else None )
|
||||
(PulseDomain.Memory.Edges.bindings edges_pre)
|
||||
(PulseDomain.Memory.Edges.bindings edges_post)
|
||||
with
|
||||
| Unequal_lengths ->
|
||||
debug "Mismatch in pre and post.\n" ;
|
||||
None
|
||||
| Ok x ->
|
||||
x
|
||||
|
||||
|
||||
let add_invalid_and_modified ~check_empty attrs acc =
|
||||
let modified =
|
||||
Attributes.get_written_to attrs
|
||||
|> Option.value_map ~default:[] ~f:(fun modified -> [ImpurityDomain.WrittenTo modified])
|
||||
in
|
||||
let res =
|
||||
Attributes.get_invalid attrs
|
||||
|> Option.value_map ~default:modified ~f:(fun invalid ->
|
||||
ImpurityDomain.Invalid invalid :: modified )
|
||||
in
|
||||
if check_empty && List.is_empty res then
|
||||
L.(die InternalError) "Address is modified without being written to or invalidated."
|
||||
else res @ acc
|
||||
|
||||
|
||||
(** Given Pulse summary, extract impurity info, i.e. parameters and
|
||||
global variables that are modified by the function.
|
||||
|
||||
TODO: keep track of impure library calls *)
|
||||
let extract_impurity pdesc pre_post : ImpurityDomain.t =
|
||||
let pre_heap = (PulseAbductiveDomain.extract_pre pre_post).PulseDomain.heap in
|
||||
let post_heap = (PulseAbductiveDomain.extract_post pre_post).PulseDomain.heap in
|
||||
let post_stack = (PulseAbductiveDomain.extract_post pre_post).PulseDomain.stack in
|
||||
let add_to_modified var addr acc =
|
||||
let rec aux acc ~addr_to_explore ~visited : ImpurityDomain.trace list =
|
||||
match addr_to_explore with
|
||||
| [] ->
|
||||
acc
|
||||
| addr :: addr_to_explore -> (
|
||||
if PulseDomain.AbstractAddressSet.mem addr visited then aux acc ~addr_to_explore ~visited
|
||||
else
|
||||
let cell_pre_opt = PulseDomain.Memory.find_opt addr pre_heap in
|
||||
let cell_post_opt = PulseDomain.Memory.find_opt addr post_heap in
|
||||
let visited = PulseDomain.AbstractAddressSet.add addr visited in
|
||||
match (cell_pre_opt, cell_post_opt) with
|
||||
| None, None ->
|
||||
aux acc ~addr_to_explore ~visited
|
||||
| Some (_, _pre_attrs), None ->
|
||||
L.(die InternalError)
|
||||
"It is unexpected to have an address which has a binding in pre but not in post!"
|
||||
| None, Some (_edges_post, attrs_post) ->
|
||||
aux
|
||||
(add_invalid_and_modified ~check_empty:false attrs_post acc)
|
||||
~addr_to_explore ~visited
|
||||
| Some (edges_pre, _), Some (edges_post, attrs_post) -> (
|
||||
match get_matching_dest_addr_opt ~edges_pre ~edges_post with
|
||||
| Some addr_list ->
|
||||
aux
|
||||
(add_invalid_and_modified attrs_post ~check_empty:false acc)
|
||||
~addr_to_explore:(addr_list @ addr_to_explore) ~visited
|
||||
| None ->
|
||||
aux
|
||||
(add_invalid_and_modified ~check_empty:true attrs_post acc)
|
||||
~addr_to_explore ~visited ) )
|
||||
in
|
||||
match aux [] ~addr_to_explore:[addr] ~visited:PulseDomain.AbstractAddressSet.empty with
|
||||
| [] ->
|
||||
acc
|
||||
| hd :: tl ->
|
||||
ImpurityDomain.ModifiedVarSet.add {var; trace_list= (hd, tl)} acc
|
||||
in
|
||||
let pname = Procdesc.get_proc_name pdesc in
|
||||
let modified_params =
|
||||
Procdesc.get_formals pdesc
|
||||
|> List.fold_left ~init:ImpurityDomain.ModifiedVarSet.empty ~f:(fun acc (name, typ) ->
|
||||
let var = Var.of_pvar (Pvar.mk name pname) in
|
||||
match BaseStack.find_opt var post_stack with
|
||||
| Some (addr, _) when Typ.is_pointer typ ->
|
||||
add_to_modified var addr acc
|
||||
| _ ->
|
||||
acc )
|
||||
in
|
||||
let modified_globals =
|
||||
BaseStack.fold
|
||||
(fun var (addr, _) acc -> if Var.is_global var then add_to_modified var addr acc else acc)
|
||||
post_stack ImpurityDomain.ModifiedVarSet.empty
|
||||
in
|
||||
{modified_globals; modified_params}
|
||||
|
||||
|
||||
let report_errors summary (ImpurityDomain.{modified_globals; modified_params} as astate) =
|
||||
let pdesc = Summary.get_proc_desc summary in
|
||||
let proc_name = Procdesc.get_proc_name pdesc in
|
||||
let pname_loc = Procdesc.get_loc pdesc in
|
||||
if Purity.should_report pdesc && not (ImpurityDomain.is_pure astate) then
|
||||
let impure_fun_desc = F.asprintf "Impure function %a" Typ.Procname.pp proc_name in
|
||||
let impure_fun_ltr = Errlog.make_trace_element 0 pname_loc impure_fun_desc [] in
|
||||
let modified_ltr ~str set acc =
|
||||
ImpurityDomain.ModifiedVarSet.fold (ImpurityDomain.add_to_errlog ~nesting:1 ~str) set acc
|
||||
in
|
||||
let ltr =
|
||||
impure_fun_ltr
|
||||
:: modified_ltr ~str:"parameter" modified_params
|
||||
(modified_ltr ~str:"global var" modified_globals [])
|
||||
in
|
||||
Reporting.log_error summary ~loc:pname_loc ~ltr IssueType.impure_function impure_fun_desc
|
||||
|
||||
|
||||
let checker ({Callbacks.summary} as callback) : Summary.t =
|
||||
let pulse_summary = Pulse.checker callback in
|
||||
match pulse_summary.payloads.pulse with
|
||||
| Some pre_posts ->
|
||||
let modified =
|
||||
List.fold pre_posts ~init:ImpurityDomain.pure ~f:(fun acc pre_post ->
|
||||
let modified = extract_impurity (Summary.get_proc_desc summary) pre_post in
|
||||
ImpurityDomain.join acc modified )
|
||||
in
|
||||
report_errors summary modified ;
|
||||
Payload.update_summary modified summary
|
||||
| None ->
|
||||
debug "@\n\n[WARNING] Couldn't find any Pulse summary to extract impurity information." ;
|
||||
summary
|
@ -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.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
module F = Format
|
||||
|
||||
type trace =
|
||||
| WrittenTo of unit PulseDomain.InterprocAction.t
|
||||
| Invalid of PulseDomain.Invalidation.t PulseDomain.Trace.t
|
||||
[@@deriving compare]
|
||||
|
||||
module ModifiedVar = struct
|
||||
type nonempty_action_type = trace * trace list [@@deriving compare]
|
||||
|
||||
type t = {var: Var.t; trace_list: nonempty_action_type} [@@deriving compare]
|
||||
|
||||
let pp fmt {var} = F.fprintf fmt "@\n %a @\n" Var.pp var
|
||||
end
|
||||
|
||||
module ModifiedVarSet = AbstractDomain.FiniteSet (ModifiedVar)
|
||||
|
||||
type t = {modified_params: ModifiedVarSet.t; modified_globals: ModifiedVarSet.t}
|
||||
|
||||
let is_pure {modified_globals; modified_params} =
|
||||
ModifiedVarSet.is_empty modified_globals && ModifiedVarSet.is_empty modified_params
|
||||
|
||||
|
||||
let pp fmt ({modified_globals; modified_params} as astate) =
|
||||
if is_pure astate then F.fprintf fmt "@\n pure @\n"
|
||||
else if ModifiedVarSet.is_empty modified_params then
|
||||
F.fprintf fmt "@\n impure, modified globals :%a @\n" ModifiedVarSet.pp modified_globals
|
||||
else if ModifiedVarSet.is_empty modified_globals then
|
||||
F.fprintf fmt "@\n impure, modified params :%a @\n" ModifiedVarSet.pp modified_params
|
||||
else
|
||||
F.fprintf fmt "@\n impure, modified params :%a, modified globals :%a @\n" ModifiedVarSet.pp
|
||||
modified_params ModifiedVarSet.pp modified_globals
|
||||
|
||||
|
||||
let pure = {modified_params= ModifiedVarSet.empty; modified_globals= ModifiedVarSet.empty}
|
||||
|
||||
let join astate1 astate2 =
|
||||
if phys_equal astate1 astate2 then astate1
|
||||
else
|
||||
let {modified_globals= mg1; modified_params= mp1} = astate1 in
|
||||
let {modified_globals= mg2; modified_params= mp2} = astate2 in
|
||||
PhysEqual.optim2
|
||||
~res:
|
||||
{ modified_globals= ModifiedVarSet.join mg1 mg2
|
||||
; modified_params= ModifiedVarSet.join mp1 mp2 }
|
||||
astate1 astate2
|
||||
|
||||
|
||||
let add_to_errlog ~nesting ~str ModifiedVar.{var; trace_list} errlog =
|
||||
let rec aux ~nesting rev_errlog action =
|
||||
match action with
|
||||
| WrittenTo (PulseDomain.InterprocAction.Immediate {location}) ->
|
||||
let rev_errlog =
|
||||
Errlog.make_trace_element nesting location
|
||||
(F.asprintf "%s '%a' is modified at %a" str Var.pp var Location.pp location)
|
||||
[]
|
||||
:: rev_errlog
|
||||
in
|
||||
List.rev_append rev_errlog errlog
|
||||
| WrittenTo (PulseDomain.InterprocAction.ViaCall {action; f; location}) ->
|
||||
aux ~nesting:(nesting + 1)
|
||||
( Errlog.make_trace_element nesting location
|
||||
(F.asprintf "%s '%a' is modified when calling %a at %a" str Var.pp var
|
||||
PulseDomain.describe_call_event f Location.pp location)
|
||||
[]
|
||||
:: rev_errlog )
|
||||
(WrittenTo action)
|
||||
| Invalid trace ->
|
||||
PulseDomain.Trace.add_to_errlog
|
||||
~header:(F.asprintf "%s '%a'" str Var.pp var)
|
||||
(fun f invalidation ->
|
||||
F.fprintf f "%a here" PulseDomain.Invalidation.describe invalidation )
|
||||
trace rev_errlog
|
||||
in
|
||||
let first_trace, rest = trace_list in
|
||||
List.fold_left rest ~init:(aux ~nesting [] first_trace) ~f:(aux ~nesting)
|
@ -0,0 +1,39 @@
|
||||
(*
|
||||
* 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.
|
||||
*)
|
||||
open! IStd
|
||||
|
||||
type trace =
|
||||
| WrittenTo of unit PulseDomain.InterprocAction.t
|
||||
| Invalid of PulseDomain.Invalidation.t PulseDomain.Trace.t
|
||||
[@@deriving compare]
|
||||
|
||||
module ModifiedVar : sig
|
||||
type nonempty_action_type = trace * trace sexp_list
|
||||
|
||||
type t = {var: Var.t; trace_list: nonempty_action_type}
|
||||
end
|
||||
|
||||
module ModifiedVarSet : sig
|
||||
include AbstractDomain.FiniteSetS with type elt = ModifiedVar.t
|
||||
end
|
||||
|
||||
type t = {modified_params: ModifiedVarSet.t; modified_globals: ModifiedVarSet.t}
|
||||
|
||||
val pure : t
|
||||
|
||||
val is_pure : t -> bool
|
||||
|
||||
val add_to_errlog :
|
||||
nesting:int
|
||||
-> str:string
|
||||
-> ModifiedVar.t
|
||||
-> Errlog.loc_trace_elem sexp_list
|
||||
-> Errlog.loc_trace_elem sexp_list
|
||||
|
||||
val pp : Format.formatter -> t -> unit
|
||||
|
||||
val join : t -> t -> t
|
@ -0,0 +1,17 @@
|
||||
# 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/errors/Makefile for the custom isystem
|
||||
CLANG_OPTIONS = -x c++ -std=c++17 -nostdinc++ -isystem$(CLANG_INCLUDES)/c++/v1/ -c
|
||||
INFER_OPTIONS = --impurity-only --debug-exceptions --project-root $(TESTS_DIR) --report-force-relative-path
|
||||
INFERPRINT_OPTIONS = --issues-tests
|
||||
|
||||
SOURCES = $(wildcard *.cpp)
|
||||
|
||||
include $(TESTS_DIR)/clang.make
|
||||
|
||||
infer-out/report.json: $(MAKEFILE_LIST)
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
// modifies a & b
|
||||
void array_mod_impure(int a[10], int b[10], int c) {
|
||||
a[0] = c;
|
||||
b[0] = c;
|
||||
}
|
||||
|
||||
// modifies a twice
|
||||
void array_mod_both_impure(int a[10], int b) {
|
||||
a[0] = b;
|
||||
a[1] = 0;
|
||||
}
|
||||
|
||||
// modifies a
|
||||
void call_array_mod_impure(int a[10]) {
|
||||
int b[10];
|
||||
array_mod_impure(a, b, 9);
|
||||
}
|
||||
|
||||
void call_array_mod_with_fresh_pure() {
|
||||
int a[10];
|
||||
array_mod_impure(a, a, 0);
|
||||
}
|
||||
|
||||
// modifies array
|
||||
void alias_mod_impure(int array[], int i, int j) {
|
||||
int* a = array;
|
||||
a[j] = i;
|
||||
}
|
||||
|
||||
struct Foo {
|
||||
int x;
|
||||
};
|
||||
|
||||
void modify_direct_impure(Foo array[10], int i, Foo foo) { array[i].x = foo.x; }
|
||||
|
||||
void modify_ptr_impure(Foo array[10], Foo foo) {
|
||||
Foo* tmp = array;
|
||||
tmp->x = foo.x;
|
||||
}
|
||||
|
||||
void call_impure_with_fresh_pure() {
|
||||
struct Foo f1 = {1};
|
||||
struct Foo f2 = {2};
|
||||
struct Foo f3 = {3};
|
||||
Foo array[2] = {f1, f2};
|
||||
modify_direct_impure(array, 0, f3);
|
||||
modify_ptr_impure(array, f3);
|
||||
}
|
||||
|
||||
// Note: Unlike C++, in Java, this is gonna be impure because
|
||||
// everything is passed by reference in Java
|
||||
int modify_by_copy_pure(Foo array[1], int i, Foo foo) {
|
||||
Foo tmp = array[i];
|
||||
tmp.x = foo.x;
|
||||
return tmp.x;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static int x;
|
||||
int a[3];
|
||||
|
||||
void modify_global_primitive_impure() { x = 10; }
|
||||
|
||||
void modify_global_array_impure() { a[0] = 0; }
|
||||
|
||||
void call_modify_global() {
|
||||
modify_global_primitive_impure();
|
||||
modify_global_array_impure();
|
||||
}
|
||||
|
||||
// modifies local static arr
|
||||
int* local_static_var_impure() {
|
||||
|
||||
// Lifetime of a static variable is throughout the program.
|
||||
static int arr[100];
|
||||
|
||||
/* Some operations on arr[] */
|
||||
arr[0] = 10;
|
||||
arr[1] = 20;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
void modify_global_inside_lamda_impure() {
|
||||
auto lam = [&]() { x++; };
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
int* global_pointer;
|
||||
|
||||
void free_global_pointer_impure() { free(global_pointer); }
|
||||
// If Pulse raises an error, it stops the world and has no summary.
|
||||
void double_free_global_impure_FN() {
|
||||
free_global_pointer_impure();
|
||||
free_global_pointer_impure();
|
||||
}
|
||||
|
||||
int free_param_impure(int* x) {
|
||||
free(x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct Simple {
|
||||
int f;
|
||||
};
|
||||
void delete_param_impure(Simple* s) { delete s; }
|
||||
|
||||
void local_deleted_pure() {
|
||||
auto* s = new Simple{1};
|
||||
delete s;
|
||||
}
|
||||
|
||||
Simple* reassign_impure(Simple* s) {
|
||||
s = new Simple{2};
|
||||
return s;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
../../facebook-clang-plugins/clang/install/include/c++/v1/iterator, std::__wrap_iter<A*>::operator++, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function std::__wrap_iter<A*>::operator++,parameter 'this' is modified at line 1390, column 9]
|
||||
../../facebook-clang-plugins/clang/install/include/c++/v1/iterator, std::__wrap_iter<int*>::operator++, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function std::__wrap_iter<int*>::operator++,parameter 'this' is modified at line 1390, column 9]
|
||||
codetoanalyze/cpp/impurity/array_test.cpp, alias_mod_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function alias_mod_impure,parameter 'array' is modified at line 33, column 3]
|
||||
codetoanalyze/cpp/impurity/array_test.cpp, array_mod_both_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function array_mod_both_impure,parameter 'a' is modified at line 15, column 3,parameter 'a' is modified at line 16, column 3]
|
||||
codetoanalyze/cpp/impurity/array_test.cpp, array_mod_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function array_mod_impure,parameter 'b' is modified at line 10, column 3,parameter 'a' is modified at line 9, column 3]
|
||||
codetoanalyze/cpp/impurity/array_test.cpp, call_array_mod_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function call_array_mod_impure,parameter 'a' is modified when calling `array_mod_impure()()` at line 22, column 3,parameter 'a' is modified at line 9, column 3]
|
||||
codetoanalyze/cpp/impurity/array_test.cpp, modify_direct_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function modify_direct_impure,parameter 'array' is modified at line 40, column 60]
|
||||
codetoanalyze/cpp/impurity/array_test.cpp, modify_ptr_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function modify_ptr_impure,parameter 'array' is modified at line 44, column 3]
|
||||
codetoanalyze/cpp/impurity/global_test.cpp, call_modify_global, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function call_modify_global,global var 'a' is modified when calling `modify_global_array_impure()()` at line 16, column 3,global var 'a' is modified at line 12, column 37,global var 'x' is modified when calling `modify_global_primitive_impure()()` at line 15, column 3,global var 'x' is modified at line 10, column 41]
|
||||
codetoanalyze/cpp/impurity/global_test.cpp, local_static_var_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function local_static_var_impure,global var 'local_static_var_impure.arr' is modified at line 26, column 3,global var 'local_static_var_impure.arr' is modified at line 27, column 3]
|
||||
codetoanalyze/cpp/impurity/global_test.cpp, modify_global_array_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function modify_global_array_impure,global var 'a' is modified at line 12, column 37]
|
||||
codetoanalyze/cpp/impurity/global_test.cpp, modify_global_inside_lamda_impure::lambda_global_test.cpp:33:14::operator(), 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function modify_global_inside_lamda_impure::lambda_global_test.cpp:33:14::operator(),global var 'x' is modified at line 33, column 22]
|
||||
codetoanalyze/cpp/impurity/global_test.cpp, modify_global_primitive_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function modify_global_primitive_impure,global var 'x' is modified at line 10, column 41]
|
||||
codetoanalyze/cpp/impurity/invalid_test.cpp, delete_param_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function delete_param_impure,parameter 's',was invalidated by `delete` here]
|
||||
codetoanalyze/cpp/impurity/invalid_test.cpp, double_free_global_impure_FN, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `free_global_pointer_impure()` here,memory was invalidated by call to `free()` here,use-after-lifetime part of the trace starts here,when calling `free_global_pointer_impure()` here,invalid access occurs here]
|
||||
codetoanalyze/cpp/impurity/invalid_test.cpp, free_global_pointer_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function free_global_pointer_impure,global var 'global_pointer',was invalidated by call to `free()` here]
|
||||
codetoanalyze/cpp/impurity/invalid_test.cpp, free_param_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function free_param_impure,parameter 'x',was invalidated by call to `free()` here]
|
||||
codetoanalyze/cpp/impurity/invalid_test.cpp, reassign_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function reassign_impure,parameter 's' is modified at line 34, column 3]
|
||||
codetoanalyze/cpp/impurity/param_test.cpp, create_cycle_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function create_cycle_impure,parameter 'x' is modified at line 23, column 44]
|
||||
codetoanalyze/cpp/impurity/param_test.cpp, invalidate_local_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function invalidate_local_impure,parameter 'pp' is modified at line 27, column 3]
|
||||
codetoanalyze/cpp/impurity/param_test.cpp, modify_mut_ref_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function modify_mut_ref_impure,parameter 'x' is modified at line 8, column 38]
|
||||
codetoanalyze/cpp/impurity/vector.cpp, assign_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function assign_impure,parameter 'vec' is modified at line 29, column 45]
|
||||
codetoanalyze/cpp/impurity/vector.cpp, clear_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function clear_impure,parameter 'vec' is modified at line 26, column 44]
|
||||
codetoanalyze/cpp/impurity/vector.cpp, insert_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function insert_impure,parameter 'vec' is modified at line 8, column 45]
|
||||
codetoanalyze/cpp/impurity/vector.cpp, push_back_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function push_back_impure,parameter 'vec' is modified at line 10, column 48]
|
||||
codetoanalyze/cpp/impurity/vector.cpp, push_back_in_loop_impure, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function push_back_in_loop_impure,parameter 'vec' is modified at line 21, column 5]
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
// modifies x (a mutable reference argument)
|
||||
void modify_mut_ref_impure(int* x) { ++*x; }
|
||||
|
||||
int primitive_pure(int x) { x++; }
|
||||
|
||||
int fact_pure(int n) {
|
||||
int f = 1;
|
||||
while (n > 0)
|
||||
f *= n--;
|
||||
return f;
|
||||
}
|
||||
|
||||
struct node {
|
||||
struct node* next;
|
||||
int data;
|
||||
};
|
||||
void create_cycle_impure(struct node* x) { x->next = x; }
|
||||
|
||||
void invalidate_local_impure(int** pp) {
|
||||
int t = 0xdeadbeef;
|
||||
*pp = &t; // <-- potential bug here since t goes out of scope
|
||||
}
|
@ -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.
|
||||
*/
|
||||
#include <iostream>
|
||||
|
||||
void output_stream_impure_FN() { std::cout << "Hello, world!" << std::endl; }
|
||||
|
||||
int random_impure_FN() { std::rand(); }
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include <vector>
|
||||
void insert_impure(std::vector<int>& vec) { vec.insert(vec.begin(), 2); }
|
||||
|
||||
void push_back_impure(std::vector<int>& vec) { vec.push_back(32); }
|
||||
|
||||
void fresh_push_back_pure() {
|
||||
std::vector<int> vec = {0, 0};
|
||||
push_back_impure(vec);
|
||||
}
|
||||
|
||||
// modifies vec
|
||||
void push_back_in_loop_impure(std::vector<int>& vec,
|
||||
std::vector<int>& vec_other) {
|
||||
for (const auto& i : vec_other) {
|
||||
vec.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// modifies vec
|
||||
void clear_impure(std::vector<int>& vec) { vec.clear(); }
|
||||
|
||||
// modifies vec
|
||||
void assign_impure(std::vector<int>& vec) { vec.assign(11, 7); }
|
||||
|
||||
struct A {
|
||||
int i;
|
||||
};
|
||||
|
||||
// iterators are not modeled in pulse yet
|
||||
int set_zero_impure_FN(std::vector<A>& numbers) {
|
||||
for (auto& num : numbers) {
|
||||
num.i = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in new issue