diff --git a/infer/models/cpp/include/infer_model/shared_ptr.h b/infer/models/cpp/include/infer_model/shared_ptr.h index e3339783c..5e5b7bb3b 100644 --- a/infer/models/cpp/include/infer_model/shared_ptr.h +++ b/infer/models/cpp/include/infer_model/shared_ptr.h @@ -240,13 +240,17 @@ class shared_ptr : public std__shared_ptr { } // observers: - - T* get() const noexcept { return model_get(__cast_to_infer_ptr(this)); } - typename add_lvalue_reference::type operator*() const noexcept { - return *model_get(__cast_to_infer_ptr(this)); + T* get() const noexcept __attribute__((deprecated( + "__infer_replace_with_deref_first_arg"))) { /* return + model_get(__cast_to_infer_ptr(this)); */ + } + typename std::add_lvalue_reference::type operator*() const noexcept + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) { + /*return *model_get(__cast_to_infer_ptr(this));*/ } - T* operator->() const noexcept { - return model_get(__cast_to_infer_ptr(this)); + T* operator->() const noexcept + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) { + /*return model_get(__cast_to_infer_ptr(this));*/ } long use_count() const noexcept { return 2; /* FIXME */ } bool unique() const noexcept { return use_count() == 1; /* FIXME */ } diff --git a/infer/src/clang/cFrontend_config.ml b/infer/src/clang/cFrontend_config.ml index 9863016b9..af4f63227 100644 --- a/infer/src/clang/cFrontend_config.ml +++ b/infer/src/clang/cFrontend_config.ml @@ -73,7 +73,8 @@ let static = "static" let string_with_utf8_m = "stringWithUTF8String:" let this = "this" let void = "void" - +let replace_with_deref_first_arg_attr = "__infer_replace_with_deref_first_arg" +let modeled_function_attributes = [replace_with_deref_first_arg_attr] (** Global state *) diff --git a/infer/src/clang/cFrontend_config.mli b/infer/src/clang/cFrontend_config.mli index 29b470c64..5d3cd72f8 100644 --- a/infer/src/clang/cFrontend_config.mli +++ b/infer/src/clang/cFrontend_config.mli @@ -73,7 +73,8 @@ val static : string val string_with_utf8_m : string val this : string val void : string - +val replace_with_deref_first_arg_attr : string +val modeled_function_attributes : string list (** Global state *) @@ -93,4 +94,3 @@ val pointer_type_index : Clang_ast_t.c_type Clang_ast_main.PointerMap.t ref (** Map from type pointers (clang pointers and types created later by frontend) to sil types Populated during frontend execution when new type is found *) val sil_types_map : (Typ.t Clang_ast_types.TypePointerMap.t) ref - diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index f028f0fa3..04f930fb5 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -441,6 +441,29 @@ struct let root_node' = GotoLabel.find_goto_label trans_state.context label_name sil_loc in { empty_res_trans with root_nodes = [root_node']; leaf_nodes = trans_state.succ_nodes } + let get_builtin_pname_opt name decl_opt = + let get_deprecated_attr_arg decl = + let open Clang_ast_t in + let decl_info = Clang_ast_proj.get_decl_tuple decl in + let get_attr_opt = function DeprecatedAttr a -> Some a | _ -> None in + match IList.find_map_opt get_attr_opt decl_info.di_attributes with + | Some attribute_info -> + (match attribute_info.ai_parameters with + | [_; arg; _] -> Some arg + | _ -> + (* it's not supposed to happen due to hardcoded exporting logic + coming from ASTExporter.h in facebook-clang-plugins *) + assert false) + | None -> None in + let function_attr_opt = Option.map_default get_deprecated_attr_arg None decl_opt in + match function_attr_opt with + | Some attr when CTrans_models.is_modeled_attribute attr -> + Some (Procname.from_string_c_fun attr) + | _ when CTrans_models.is_modeled_builtin name -> + Some (Procname.from_string_c_fun (CFrontend_config.infer ^ name)) + | _ -> None + + let function_deref_trans trans_state decl_ref = let open CContext in let context = trans_state.context in @@ -449,10 +472,9 @@ struct Option.may (call_translation context) decl_opt; let name = Ast_utils.get_qualified_name name_info in let typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in - let pname = - if CTrans_models.is_modeled_builtin name then - Procname.from_string_c_fun (CFrontend_config.infer ^ name) - else CMethod_trans.create_procdesc_with_pointer context decl_ptr None name in + let pname = match get_builtin_pname_opt name decl_opt with + | Some builtin_pname -> builtin_pname + | None -> CMethod_trans.create_procdesc_with_pointer context decl_ptr None name in let address_of_function = not context.CContext.is_callee_expression in (* If we are not translating a callee expression, *) (* then the address of the function is being taken.*) @@ -551,10 +573,17 @@ struct [], [] in (* consider using context.CContext.is_callee_expression to deal with pointers to methods? *) (* unlike field access, for method calls there is no need to expand class type *) - let pname = CMethod_trans.create_procdesc_with_pointer context decl_ptr (Some class_name) - method_name in + + (* use qualified method name for builtin matching, but use unqualified name elsewhere *) + let qual_method_name = Ast_utils.get_qualified_name name_info in + let pname = match get_builtin_pname_opt qual_method_name decl_opt with + | Some builtin_pname -> builtin_pname + | None -> + let pname = CMethod_trans.create_procdesc_with_pointer context decl_ptr (Some class_name) + method_name in + Cfg.set_procname_priority context.CContext.cfg pname; + pname in let method_exp = (Exp.Const (Const.Cfun pname), method_typ) in - Cfg.set_procname_priority context.CContext.cfg pname; { pre_trans_result with is_cpp_call_virtual = is_cpp_virtual; exps = [method_exp] @ extra_exps; @@ -925,7 +954,7 @@ struct result_trans_callee :: res_trans_p in (* first expr is method address, rest are params including 'this' parameter *) let actual_params = IList.tl (collect_exprs result_trans_subexprs) in - match cxx_method_builtin_trans trans_state_pri sil_loc callee_pname with + match cxx_method_builtin_trans trans_state_pri sil_loc result_trans_subexprs callee_pname with | Some builtin -> builtin | _ -> let call_flags = { diff --git a/infer/src/clang/cTrans_models.ml b/infer/src/clang/cTrans_models.ml index 64b732596..9efe124b2 100644 --- a/infer/src/clang/cTrans_models.ml +++ b/infer/src/clang/cTrans_models.ml @@ -31,7 +31,10 @@ let is_builtin_expect pname = Procname.to_string pname = CFrontend_config.builtin_expect let is_builtin_object_size pname = - (Procname.to_string pname) == CFrontend_config.builtin_object_size + (Procname.to_string pname) = CFrontend_config.builtin_object_size + +let is_replace_with_deref_first_arg pname = + (Procname.to_string pname) = CFrontend_config.replace_with_deref_first_arg_attr let rec get_func_type_from_stmt stmt = match stmt with @@ -72,6 +75,9 @@ let get_builtinname method_name = let is_modeled_builtin funct = funct = CFrontend_config.builtin_memset_chk +let is_modeled_attribute attr_name = + IList.mem string_equal attr_name CFrontend_config.modeled_function_attributes + let is_assert_log_s funct = funct = CFrontend_config.assert_rtn || funct = CFrontend_config.assert_fail || diff --git a/infer/src/clang/cTrans_models.mli b/infer/src/clang/cTrans_models.mli index a6fede5d5..1e9607f35 100644 --- a/infer/src/clang/cTrans_models.mli +++ b/infer/src/clang/cTrans_models.mli @@ -19,6 +19,8 @@ val is_builtin_expect : Procname.t -> bool val is_builtin_object_size : Procname.t -> bool +val is_replace_with_deref_first_arg : Procname.t -> bool + val is_objc_memory_model_controlled : string -> bool val builtin_predefined_model : Clang_ast_t.stmt -> Procname.t option -> Procname.t option * bool @@ -29,6 +31,8 @@ val is_handleFailureInMethod : string -> bool val is_modeled_builtin : string -> bool +val is_modeled_attribute : string -> bool + val is_toll_free_bridging : Procname.t -> bool val get_predefined_model_method_signature : string -> string -> diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index da4dd89b0..e74e2b203 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -467,6 +467,17 @@ let trans_builtin_expect params_trans_res = | [_; fst_arg_res; _] -> Some fst_arg_res | _ -> None +let trans_replace_with_deref_first_arg sil_loc params_trans_res ~cxx_method_call = + let first_arg_res_trans = match params_trans_res with + | _ :: fst_arg_res :: _ when not cxx_method_call -> fst_arg_res + | ({exps= _method_exp :: this_exp} as fst_arg_res) :: _ when cxx_method_call -> + (* method_deref_trans uses different format to store first argument - it stores + two things in exps: [method_exp; this_exp]. + We need to get rid of first exp before calling dereference_value_from_result *) + { fst_arg_res with exps = this_exp } + | _ -> assert false in + dereference_value_from_result sil_loc first_arg_res_trans ~strip_pointer:true + let builtin_trans trans_state loc stmt_info function_type params_trans_res pname = if CTrans_models.is_cf_non_null_alloc pname || CTrans_models.is_alloc_model function_type pname then @@ -477,11 +488,15 @@ let builtin_trans trans_state loc stmt_info function_type params_trans_res pname Some (trans_assertion trans_state loc) else if CTrans_models.is_builtin_expect pname then trans_builtin_expect params_trans_res + else if CTrans_models.is_replace_with_deref_first_arg pname then + Some (trans_replace_with_deref_first_arg loc params_trans_res ~cxx_method_call:false) else None -let cxx_method_builtin_trans trans_state loc pname = +let cxx_method_builtin_trans trans_state loc params_trans_res pname = if CTrans_models.is_assert_log pname then Some (trans_assertion trans_state loc) + else if CTrans_models.is_replace_with_deref_first_arg pname then + Some (trans_replace_with_deref_first_arg loc params_trans_res ~cxx_method_call:true) else None diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index 7d38998fc..556bf32a3 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -101,8 +101,8 @@ val get_decl_ref_info : Clang_ast_t.stmt -> Clang_ast_t.decl_ref val builtin_trans : trans_state -> Location.t -> Clang_ast_t.stmt_info -> Typ.t -> trans_result list -> Procname.t -> trans_result option -val cxx_method_builtin_trans : trans_state -> Location.t -> Procname.t -> - trans_result option +val cxx_method_builtin_trans : trans_state -> Location.t -> trans_result list -> + Procname.t -> trans_result option val alloc_trans : trans_state -> Location.t -> Clang_ast_t.stmt_info -> Typ.t -> bool -> diff --git a/infer/tests/codetoanalyze/cpp/frontend/attributes/deprecated_hack.cpp b/infer/tests/codetoanalyze/cpp/frontend/attributes/deprecated_hack.cpp new file mode 100644 index 000000000..fd038fd78 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/attributes/deprecated_hack.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +// header required by TranslateAsPtr class. It's not in common header search +// path when running clang without infer (clang wrappers add it) +// Add -isystem '/path/models/cpp/include' to clang invocation to work around +// compilation problem +#include + +/* Test for passing function attributes to infer via __deprecated__ attribute */ + +// basic test of C function with __infer_replace_with_deref_first_arg attribute +int derefFirstArg(int* a, int* b) + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) { + /* equivalent in real code: + return *a; */ +} + +int derefFirstArg2(int* a, int* b) + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) { + /* equivalent in real code: + return *a; */ + return *b; // body is in conflict with the attribute, attribute semantics + // should be used +} + +int derefFirstArg3(int* a, int* b) __attribute__((deprecated("__infer_typo"))) { + /* equivalent in real code: */ + return *b; // there isn't any known attribute with this name, use semantics + // from the body +} + +int derefFirstArg_null_deref() { + int a = 0; + return derefFirstArg(nullptr, &a); +} + +int derefFirstArg_ok_deref() { + int a = 0; + return derefFirstArg(&a, nullptr); +} + +int derefFirstArg2_null_deref() { + int a = 0; + return derefFirstArg2(nullptr, &a); +} + +int derefFirstArg2_ok_deref() { + int a = 0; + return derefFirstArg2(&a, nullptr); +} + +int derefFirstArg3_ok_deref() { + int a = 0; + return derefFirstArg3(nullptr, &a); +} + +int derefFirstArg3_null_deref() { + int a = 0; + return derefFirstArg3(&a, nullptr); +} + +// more involving test of __infer_replace_with_deref_first_arg attribute in +// context of C++ +// methods +/* This class be translated as T* by infer frontend - same as shared_ptr + This class has different API in order to better test __deprecated__ attribute + handling by the frontend. */ +template +struct TranslateAsPtr { + friend class infer_traits::TranslateAsType; + TranslateAsPtr(T* t = nullptr) { setPtr(t); } + /* calls to those functions are supposed to be translated as `*this` */ + T* getPtr() + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} + T* getPtr(int a, int b) + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} + /* calls to those functions are supposed to be translated as `**this` */ + T& operator*() + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} + T& getRef() + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} + T& getRef(int a, int b) + __attribute__((deprecated("__infer_replace_with_deref_first_arg"))) {} + + // same trick we do for setting value of shared_ptr, look there for details + void setPtr(T* v) { *((void**)(this)) = v; } +}; + +int getPtr_null_deref1() { + TranslateAsPtr t; + t.setPtr(nullptr); + return *t.getPtr(); +} + +int getPtr_null_deref2() { + TranslateAsPtr t; + t.setPtr(nullptr); + return *t.getPtr(1, 2); +} + +int getPtr_ok_deref() { + int a = 0; + TranslateAsPtr t; + t.setPtr(&a); + return *t.getPtr(); +} + +int operator_star_null_deref1() { + TranslateAsPtr t; + t.setPtr(nullptr); + return *t; // call operator* via operator call +} + +int operator_star_null_deref2() { + TranslateAsPtr t; + t.setPtr(nullptr); + return t.operator*(); // call operator* via regular method call +} + +int operator_star_ok_deref() { + int a; + TranslateAsPtr t; + t.setPtr(&a); + return t.operator*(); // call operator* via regular method call +} + +int getRef_null_deref1() { + TranslateAsPtr t; + t.setPtr(nullptr); + return t.getRef(); +} + +int getRef_null_deref2() { + TranslateAsPtr t; + t.setPtr(nullptr); + return t.getRef(1, 2); +} + +int getRef_ok_deref() { + int a = 0; + TranslateAsPtr t; + t.setPtr(&a); + return t.getRef(); +} diff --git a/infer/tests/codetoanalyze/cpp/frontend/attributes/deprecated_hack.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/attributes/deprecated_hack.cpp.dot new file mode 100644 index 000000000..ded0f6c3d --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/attributes/deprecated_hack.cpp.dot @@ -0,0 +1,358 @@ +/* @generated */ +digraph iCFG { +95 [label="95: DeclStmt \n *&a:int =0 [line 147]\n " shape="box"] + + + 95 -> 94 ; +94 [label="94: DeclStmt \n _fun_TranslateAsPtr_TranslateAsPtr(&t:int **,null:int *) [line 148]\n n$4=*&t:int * [line 148]\n " shape="box"] + + + 94 -> 93 ; +93 [label="93: Call _fun_TranslateAsPtr_setPtr \n _=*&t:int * [line 149]\n _fun_TranslateAsPtr_setPtr(&t:int *&,&a:int *) [line 149]\n " shape="box"] + + + 93 -> 92 ; +92 [label="92: Return Stmt \n _=*&t:int * [line 150]\n n$1=*&t:int *& [line 150]\n n$2=*n$1:int * [line 150]\n *&return:int =n$2 [line 150]\n " shape="box"] + + + 92 -> 91 ; +91 [label="91: Exit getRef_ok_deref \n " color=yellow style=filled] + + +90 [label="90: Start getRef_ok_deref\nFormals: \nLocals: t:int * a:int \n DECLARE_LOCALS(&return,&t,&a); [line 146]\n " color=yellow style=filled] + + + 90 -> 95 ; +89 [label="89: DeclStmt \n _fun_TranslateAsPtr_TranslateAsPtr(&t:int **,null:int *) [line 141]\n n$4=*&t:int * [line 141]\n " shape="box"] + + + 89 -> 88 ; +88 [label="88: Call _fun_TranslateAsPtr_setPtr \n _=*&t:int * [line 142]\n _fun_TranslateAsPtr_setPtr(&t:int *&,null:int *) [line 142]\n " shape="box"] + + + 88 -> 87 ; +87 [label="87: Return Stmt \n _=*&t:int * [line 143]\n n$1=*&t:int *& [line 143]\n n$2=*n$1:int * [line 143]\n *&return:int =n$2 [line 143]\n " shape="box"] + + + 87 -> 86 ; +86 [label="86: Exit getRef_null_deref2 \n " color=yellow style=filled] + + +85 [label="85: Start getRef_null_deref2\nFormals: \nLocals: t:int * \n DECLARE_LOCALS(&return,&t); [line 140]\n " color=yellow style=filled] + + + 85 -> 89 ; +84 [label="84: DeclStmt \n _fun_TranslateAsPtr_TranslateAsPtr(&t:int **,null:int *) [line 135]\n n$4=*&t:int * [line 135]\n " shape="box"] + + + 84 -> 83 ; +83 [label="83: Call _fun_TranslateAsPtr_setPtr \n _=*&t:int * [line 136]\n _fun_TranslateAsPtr_setPtr(&t:int *&,null:int *) [line 136]\n " shape="box"] + + + 83 -> 82 ; +82 [label="82: Return Stmt \n _=*&t:int * [line 137]\n n$1=*&t:int *& [line 137]\n n$2=*n$1:int * [line 137]\n *&return:int =n$2 [line 137]\n " shape="box"] + + + 82 -> 81 ; +81 [label="81: Exit getRef_null_deref1 \n " color=yellow style=filled] + + +80 [label="80: Start getRef_null_deref1\nFormals: \nLocals: t:int * \n DECLARE_LOCALS(&return,&t); [line 134]\n " color=yellow style=filled] + + + 80 -> 84 ; +79 [label="79: DeclStmt \n _fun_TranslateAsPtr_TranslateAsPtr(&t:int **,null:int *) [line 129]\n n$4=*&t:int * [line 129]\n " shape="box"] + + + 79 -> 78 ; +78 [label="78: Call _fun_TranslateAsPtr_setPtr \n _=*&t:int * [line 130]\n _fun_TranslateAsPtr_setPtr(&t:int *&,&a:int *) [line 130]\n " shape="box"] + + + 78 -> 77 ; +77 [label="77: Return Stmt \n _=*&t:int * [line 131]\n n$1=*&t:int *& [line 131]\n n$2=*n$1:int * [line 131]\n *&return:int =n$2 [line 131]\n " shape="box"] + + + 77 -> 76 ; +76 [label="76: Exit operator_star_ok_deref \n " color=yellow style=filled] + + +75 [label="75: Start operator_star_ok_deref\nFormals: \nLocals: t:int * a:int \n DECLARE_LOCALS(&return,&t,&a); [line 127]\n " color=yellow style=filled] + + + 75 -> 79 ; +74 [label="74: DeclStmt \n _fun_TranslateAsPtr_TranslateAsPtr(&t:int **,null:int *) [line 122]\n n$4=*&t:int * [line 122]\n " shape="box"] + + + 74 -> 73 ; +73 [label="73: Call _fun_TranslateAsPtr_setPtr \n _=*&t:int * [line 123]\n _fun_TranslateAsPtr_setPtr(&t:int *&,null:int *) [line 123]\n " shape="box"] + + + 73 -> 72 ; +72 [label="72: Return Stmt \n _=*&t:int * [line 124]\n n$1=*&t:int *& [line 124]\n n$2=*n$1:int * [line 124]\n *&return:int =n$2 [line 124]\n " shape="box"] + + + 72 -> 71 ; +71 [label="71: Exit operator_star_null_deref2 \n " color=yellow style=filled] + + +70 [label="70: Start operator_star_null_deref2\nFormals: \nLocals: t:int * \n DECLARE_LOCALS(&return,&t); [line 121]\n " color=yellow style=filled] + + + 70 -> 74 ; +69 [label="69: DeclStmt \n _fun_TranslateAsPtr_TranslateAsPtr(&t:int **,null:int *) [line 116]\n n$3=*&t:int * [line 116]\n " shape="box"] + + + 69 -> 68 ; +68 [label="68: Call _fun_TranslateAsPtr_setPtr \n _=*&t:int * [line 117]\n _fun_TranslateAsPtr_setPtr(&t:int *&,null:int *) [line 117]\n " shape="box"] + + + 68 -> 67 ; +67 [label="67: Return Stmt \n n$0=*&t:int *& [line 118]\n n$1=*n$0:int * [line 118]\n *&return:int =n$1 [line 118]\n " shape="box"] + + + 67 -> 66 ; +66 [label="66: Exit operator_star_null_deref1 \n " color=yellow style=filled] + + +65 [label="65: Start operator_star_null_deref1\nFormals: \nLocals: t:int * \n DECLARE_LOCALS(&return,&t); [line 115]\n " color=yellow style=filled] + + + 65 -> 69 ; +64 [label="64: DeclStmt \n *&a:int =0 [line 109]\n " shape="box"] + + + 64 -> 63 ; +63 [label="63: DeclStmt \n _fun_TranslateAsPtr_TranslateAsPtr(&t:int **,null:int *) [line 110]\n n$4=*&t:int * [line 110]\n " shape="box"] + + + 63 -> 62 ; +62 [label="62: Call _fun_TranslateAsPtr_setPtr \n _=*&t:int * [line 111]\n _fun_TranslateAsPtr_setPtr(&t:int *&,&a:int *) [line 111]\n " shape="box"] + + + 62 -> 61 ; +61 [label="61: Return Stmt \n _=*&t:int * [line 112]\n n$1=*&t:int *& [line 112]\n n$2=*n$1:int [line 112]\n *&return:int =n$2 [line 112]\n " shape="box"] + + + 61 -> 60 ; +60 [label="60: Exit getPtr_ok_deref \n " color=yellow style=filled] + + +59 [label="59: Start getPtr_ok_deref\nFormals: \nLocals: t:int * a:int \n DECLARE_LOCALS(&return,&t,&a); [line 108]\n " color=yellow style=filled] + + + 59 -> 64 ; +58 [label="58: DeclStmt \n _fun_TranslateAsPtr_TranslateAsPtr(&t:int **,null:int *) [line 103]\n n$4=*&t:int * [line 103]\n " shape="box"] + + + 58 -> 57 ; +57 [label="57: Call _fun_TranslateAsPtr_setPtr \n _=*&t:int * [line 104]\n _fun_TranslateAsPtr_setPtr(&t:int *&,null:int *) [line 104]\n " shape="box"] + + + 57 -> 56 ; +56 [label="56: Return Stmt \n _=*&t:int * [line 105]\n n$1=*&t:int *& [line 105]\n n$2=*n$1:int [line 105]\n *&return:int =n$2 [line 105]\n " shape="box"] + + + 56 -> 55 ; +55 [label="55: Exit getPtr_null_deref2 \n " color=yellow style=filled] + + +54 [label="54: Start getPtr_null_deref2\nFormals: \nLocals: t:int * \n DECLARE_LOCALS(&return,&t); [line 102]\n " color=yellow style=filled] + + + 54 -> 58 ; +53 [label="53: DeclStmt \n _fun_TranslateAsPtr_TranslateAsPtr(&t:int **,null:int *) [line 97]\n n$4=*&t:int * [line 97]\n " shape="box"] + + + 53 -> 52 ; +52 [label="52: Call _fun_TranslateAsPtr_setPtr \n _=*&t:int * [line 98]\n _fun_TranslateAsPtr_setPtr(&t:int *&,null:int *) [line 98]\n " shape="box"] + + + 52 -> 51 ; +51 [label="51: Return Stmt \n _=*&t:int * [line 99]\n n$1=*&t:int *& [line 99]\n n$2=*n$1:int [line 99]\n *&return:int =n$2 [line 99]\n " shape="box"] + + + 51 -> 50 ; +50 [label="50: Exit getPtr_null_deref1 \n " color=yellow style=filled] + + +49 [label="49: Start getPtr_null_deref1\nFormals: \nLocals: t:int * \n DECLARE_LOCALS(&return,&t); [line 96]\n " color=yellow style=filled] + + + 49 -> 53 ; +48 [label="48: Exit TranslateAsPtr_getRef \n " color=yellow style=filled] + + +47 [label="47: Start TranslateAsPtr_getRef\nFormals: this:int ** a:int b:int \nLocals: \n DECLARE_LOCALS(&return); [line 89]\n " color=yellow style=filled] + + + 47 -> 48 ; +46 [label="46: Exit TranslateAsPtr_getRef \n " color=yellow style=filled] + + +45 [label="45: Start TranslateAsPtr_getRef\nFormals: this:int **\nLocals: \n DECLARE_LOCALS(&return); [line 87]\n " color=yellow style=filled] + + + 45 -> 46 ; +44 [label="44: Exit TranslateAsPtr_operator* \n " color=yellow style=filled] + + +43 [label="43: Start TranslateAsPtr_operator*\nFormals: this:int **\nLocals: \n DECLARE_LOCALS(&return); [line 85]\n " color=yellow style=filled] + + + 43 -> 44 ; +42 [label="42: Exit TranslateAsPtr_getPtr \n " color=yellow style=filled] + + +41 [label="41: Start TranslateAsPtr_getPtr\nFormals: this:int ** a:int b:int \nLocals: \n DECLARE_LOCALS(&return); [line 82]\n " color=yellow style=filled] + + + 41 -> 42 ; +40 [label="40: Exit TranslateAsPtr_getPtr \n " color=yellow style=filled] + + +39 [label="39: Start TranslateAsPtr_getPtr\nFormals: this:int **\nLocals: \n DECLARE_LOCALS(&return); [line 80]\n " color=yellow style=filled] + + + 39 -> 40 ; +38 [label="38: Call _fun_TranslateAsPtr_setPtr \n n$0=*&this:int ** [line 78]\n _=*n$0:int * [line 78]\n n$2=*&t:int * [line 78]\n _fun_TranslateAsPtr_setPtr(n$0:int **,n$2:int *) [line 78]\n " shape="box"] + + + 38 -> 34 ; +37 [label="37: BinaryOperatorStmt: Assign \n n$0=*&this:int ** [line 93]\n n$1=*&v:int * [line 93]\n *n$0:void *=n$1 [line 93]\n " shape="box"] + + + 37 -> 36 ; +36 [label="36: Exit TranslateAsPtr_setPtr \n " color=yellow style=filled] + + +35 [label="35: Start TranslateAsPtr_setPtr\nFormals: this:int ** v:int *\nLocals: \n DECLARE_LOCALS(&return); [line 93]\n " color=yellow style=filled] + + + 35 -> 37 ; +34 [label="34: Exit TranslateAsPtr_TranslateAsPtr \n " color=yellow style=filled] + + +33 [label="33: Start TranslateAsPtr_TranslateAsPtr\nFormals: this:int ** t:int *\nLocals: \n DECLARE_LOCALS(&return); [line 78]\n " color=yellow style=filled] + + + 33 -> 38 ; +32 [label="32: DeclStmt \n *&a:int =0 [line 65]\n " shape="box"] + + + 32 -> 31 ; +31 [label="31: Return Stmt \n n$0=_fun_derefFirstArg3(&a:int *,null:int *) [line 66]\n *&return:int =n$0 [line 66]\n " shape="box"] + + + 31 -> 30 ; +30 [label="30: Exit derefFirstArg3_null_deref \n " color=yellow style=filled] + + +29 [label="29: Start derefFirstArg3_null_deref\nFormals: \nLocals: a:int \n DECLARE_LOCALS(&return,&a); [line 64]\n " color=yellow style=filled] + + + 29 -> 32 ; +28 [label="28: DeclStmt \n *&a:int =0 [line 60]\n " shape="box"] + + + 28 -> 27 ; +27 [label="27: Return Stmt \n n$0=_fun_derefFirstArg3(null:int *,&a:int *) [line 61]\n *&return:int =n$0 [line 61]\n " shape="box"] + + + 27 -> 26 ; +26 [label="26: Exit derefFirstArg3_ok_deref \n " color=yellow style=filled] + + +25 [label="25: Start derefFirstArg3_ok_deref\nFormals: \nLocals: a:int \n DECLARE_LOCALS(&return,&a); [line 59]\n " color=yellow style=filled] + + + 25 -> 28 ; +24 [label="24: DeclStmt \n *&a:int =0 [line 55]\n " shape="box"] + + + 24 -> 23 ; +23 [label="23: Return Stmt \n n$0=*&a:int * [line 56]\n *&return:int =n$0 [line 56]\n " shape="box"] + + + 23 -> 22 ; +22 [label="22: Exit derefFirstArg2_ok_deref \n " color=yellow style=filled] + + +21 [label="21: Start derefFirstArg2_ok_deref\nFormals: \nLocals: a:int \n DECLARE_LOCALS(&return,&a); [line 54]\n " color=yellow style=filled] + + + 21 -> 24 ; +20 [label="20: DeclStmt \n *&a:int =0 [line 50]\n " shape="box"] + + + 20 -> 19 ; +19 [label="19: Return Stmt \n n$0=*null:int * [line 51]\n *&return:int =n$0 [line 51]\n " shape="box"] + + + 19 -> 18 ; +18 [label="18: Exit derefFirstArg2_null_deref \n " color=yellow style=filled] + + +17 [label="17: Start derefFirstArg2_null_deref\nFormals: \nLocals: a:int \n DECLARE_LOCALS(&return,&a); [line 49]\n " color=yellow style=filled] + + + 17 -> 20 ; +16 [label="16: DeclStmt \n *&a:int =0 [line 45]\n " shape="box"] + + + 16 -> 15 ; +15 [label="15: Return Stmt \n n$0=*&a:int * [line 46]\n *&return:int =n$0 [line 46]\n " shape="box"] + + + 15 -> 14 ; +14 [label="14: Exit derefFirstArg_ok_deref \n " color=yellow style=filled] + + +13 [label="13: Start derefFirstArg_ok_deref\nFormals: \nLocals: a:int \n DECLARE_LOCALS(&return,&a); [line 44]\n " color=yellow style=filled] + + + 13 -> 16 ; +12 [label="12: DeclStmt \n *&a:int =0 [line 40]\n " shape="box"] + + + 12 -> 11 ; +11 [label="11: Return Stmt \n n$0=*null:int * [line 41]\n *&return:int =n$0 [line 41]\n " shape="box"] + + + 11 -> 10 ; +10 [label="10: Exit derefFirstArg_null_deref \n " color=yellow style=filled] + + +9 [label="9: Start derefFirstArg_null_deref\nFormals: \nLocals: a:int \n DECLARE_LOCALS(&return,&a); [line 39]\n " color=yellow style=filled] + + + 9 -> 12 ; +8 [label="8: Return Stmt \n n$0=*&b:int * [line 35]\n n$1=*n$0:int [line 35]\n *&return:int =n$1 [line 35]\n " shape="box"] + + + 8 -> 7 ; +7 [label="7: Exit derefFirstArg3 \n " color=yellow style=filled] + + +6 [label="6: Start derefFirstArg3\nFormals: a:int * b:int *\nLocals: \n DECLARE_LOCALS(&return); [line 33]\n " color=yellow style=filled] + + + 6 -> 8 ; +5 [label="5: Return Stmt \n n$0=*&b:int * [line 29]\n n$1=*n$0:int [line 29]\n *&return:int =n$1 [line 29]\n " shape="box"] + + + 5 -> 4 ; +4 [label="4: Exit derefFirstArg2 \n " color=yellow style=filled] + + +3 [label="3: Start derefFirstArg2\nFormals: a:int * b:int *\nLocals: \n DECLARE_LOCALS(&return); [line 25]\n " color=yellow style=filled] + + + 3 -> 5 ; +2 [label="2: Exit derefFirstArg \n " color=yellow style=filled] + + +1 [label="1: Start derefFirstArg\nFormals: a:int * b:int *\nLocals: \n DECLARE_LOCALS(&return); [line 19]\n " color=yellow style=filled] + + + 1 -> 2 ; +} diff --git a/infer/tests/endtoend/cpp/infer/DeprecatedHackTest.java b/infer/tests/endtoend/cpp/infer/DeprecatedHackTest.java new file mode 100644 index 000000000..84005b37b --- /dev/null +++ b/infer/tests/endtoend/cpp/infer/DeprecatedHackTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package endtoend.cpp.infer; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.ResultContainsExactly.containsExactly; + +import com.google.common.collect.ImmutableList; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferResults; +import utils.InferRunner; + +public class DeprecatedHackTest { + + public static final String SOURCE_FILE = + "infer/tests/codetoanalyze/cpp/frontend/attributes/deprecated_hack.cpp"; + + public static final String NULL_DEREFERENCE = "NULL_DEREFERENCE"; + private static ImmutableList inferCmd; + + @ClassRule + public static DebuggableTemporaryFolder folder = + new DebuggableTemporaryFolder(); + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferCmd = InferRunner.createCPPInferCommand(folder, SOURCE_FILE); + } + + @Test + public void whenInferRunsOnNullDerefThenNullDereferenceIsFound() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferC(inferCmd); + String[] procedures = { + "derefFirstArg_null_deref", + "derefFirstArg2_null_deref", + "derefFirstArg3_null_deref", + "getPtr_null_deref1", + "getPtr_null_deref2", + "operator_star_null_deref1", + "operator_star_null_deref2", + "getRef_null_deref1", + "getRef_null_deref2", + }; + assertThat( + "Results should contain divide by zero error", + inferResults, + containsExactly( + NULL_DEREFERENCE, + SOURCE_FILE, + procedures + ) + ); + } + + +} diff --git a/infer/tests/frontend/cpp/AttributesTest.java b/infer/tests/frontend/cpp/AttributesTest.java index 4cc63f1c4..3eddc93b3 100644 --- a/infer/tests/frontend/cpp/AttributesTest.java +++ b/infer/tests/frontend/cpp/AttributesTest.java @@ -35,4 +35,9 @@ public class AttributesTest { frontendTest("clang_fallthrough.cpp"); } + @Test + public void testDeprecatedHackDotFilesMatch() + throws InterruptedException, IOException, InferException { + frontendTest("deprecated_hack.cpp"); + } }