diff --git a/infer/src/quandary/TaintAnalysis.ml b/infer/src/quandary/TaintAnalysis.ml index 913706036..800a7a785 100644 --- a/infer/src/quandary/TaintAnalysis.ml +++ b/infer/src/quandary/TaintAnalysis.ml @@ -265,6 +265,13 @@ module Make (TaintSpecification : TaintSpec.S) = struct let exec_instr (astate : Domain.astate) (proc_data : FormalMap.t ProcData.t) _ (instr : HilInstr.t) = + let exec_write lhs_access_path rhs_exp astate= + let rhs_node = + Option.value + (hil_exp_get_node rhs_exp astate proc_data) + ~default:TaintDomain.empty_node in + TaintDomain.add_node (AccessPath.Exact lhs_access_path) rhs_node astate in + match instr with | Write (((Var.ProgramVar pvar, _), []), HilExp.Exception _, _) when Pvar.is_return pvar -> (* the Java frontend translates `throw Exception` as `return Exception`, which is a bit @@ -281,11 +288,7 @@ module Make (TaintSpecification : TaintSpec.S) = struct astate | Write (lhs_access_path, rhs_exp, _) -> - let rhs_node = - Option.value - (hil_exp_get_node rhs_exp astate proc_data) - ~default:TaintDomain.empty_node in - TaintDomain.add_node (AccessPath.Exact lhs_access_path) rhs_node astate + exec_write lhs_access_path rhs_exp astate | Call (ret_opt, Direct called_pname, actuals, call_flags, callee_loc) -> let handle_unknown_call callee_pname access_tree = @@ -336,13 +339,24 @@ module Make (TaintSpecification : TaintSpec.S) = struct | _ -> astate_acc in - let propagations = - TaintSpecification.handle_unknown_call - callee_pname - (Option.map ~f:snd ret_opt) - actuals - proc_data.tenv in - List.fold ~f:handle_unknown_call_ ~init:access_tree propagations in + match Typ.Procname.get_method callee_pname with + | "operator=" when Typ.Procname.is_c_method callee_pname -> + (* treat unknown calls to C++ operator= as assignment *) + begin + match actuals with + | [AccessPath lhs_access_path; rhs_exp] -> + exec_write lhs_access_path rhs_exp access_tree + | _ -> + failwithf "Unexpected call to operator= %a" HilInstr.pp instr + end + | _ -> + let propagations = + TaintSpecification.handle_unknown_call + callee_pname + (Option.map ~f:snd ret_opt) + actuals + proc_data.tenv in + List.fold ~f:handle_unknown_call_ ~init:access_tree propagations in let analyze_call astate_acc callee_pname = let call_site = CallSite.make callee_pname callee_loc in diff --git a/infer/tests/codetoanalyze/cpp/quandary/.inferconfig b/infer/tests/codetoanalyze/cpp/quandary/.inferconfig index 895c8bfdb..6eb0f03d2 100644 --- a/infer/tests/codetoanalyze/cpp/quandary/.inferconfig +++ b/infer/tests/codetoanalyze/cpp/quandary/.inferconfig @@ -37,6 +37,11 @@ "kind": "Other", "index": "0" }, + { + "procedure": "basics::template_sink", + "kind": "Other", + "index": "0" + }, { "procedure": "basics::Obj::string_sink", "kind": "Other", diff --git a/infer/tests/codetoanalyze/cpp/quandary/basics.cpp b/infer/tests/codetoanalyze/cpp/quandary/basics.cpp index af46c37a2..44c5a0b02 100644 --- a/infer/tests/codetoanalyze/cpp/quandary/basics.cpp +++ b/infer/tests/codetoanalyze/cpp/quandary/basics.cpp @@ -22,6 +22,8 @@ class Obj { static void static_sink(void*) {} std::string string_source(int i) { return ""; } void string_sink(std::string) {} + std::string field1; + std::string field2; }; void* returnSource() { return __infer_taint_source(); } @@ -68,6 +70,9 @@ T* template_source() { return nullptr; } +template +void template_sink(T) {} + void template_source_bad() { void* source = template_source(); __infer_taint_sink(source); @@ -77,4 +82,28 @@ void string_source_bad(Obj obj) { std::string source = obj.string_source(5); obj.string_sink(source); } + +void via_field_bad1() { + Obj* obj = new Obj(); + obj->field1 = *template_source(); + template_sink(obj->field1); +} + +void via_field_bad2(Obj* obj) { + obj->field1 = *template_source(); + template_sink(obj->field1); +} + +void via_field_ok1() { + Obj* obj = new Obj(); + obj->field1 = *template_source(); + obj->field1 = nullptr; + template_sink(obj->field1); +} + +void via_field_ok2() { + Obj* obj = new Obj(); + obj->field1 = *template_source(); + template_sink(obj->field2); +} } diff --git a/infer/tests/codetoanalyze/cpp/quandary/issues.exp b/infer/tests/codetoanalyze/cpp/quandary/issues.exp index ef72ccee6..a27beb209 100644 --- a/infer/tests/codetoanalyze/cpp/quandary/issues.exp +++ b/infer/tests/codetoanalyze/cpp/quandary/issues.exp @@ -6,6 +6,8 @@ codetoanalyze/cpp/quandary/basics.cpp, basics::sourceToSinkDirectBad, 2, QUANDAR codetoanalyze/cpp/quandary/basics.cpp, basics::static_source_sink_bad, 2, QUANDARY_TAINT_ERROR, [return from basics::Obj_static_source,call to basics::Obj_static_sink] codetoanalyze/cpp/quandary/basics.cpp, basics::string_source_bad, 2, QUANDARY_TAINT_ERROR, [return from basics::Obj_string_source,call to basics::Obj_string_sink] codetoanalyze/cpp/quandary/basics.cpp, basics::template_source_bad, 2, QUANDARY_TAINT_ERROR, [return from basics::template_source,call to __infer_taint_sink] +codetoanalyze/cpp/quandary/basics.cpp, basics::via_field_bad1, 3, QUANDARY_TAINT_ERROR, [return from basics::template_source_>,call to basics::template_sink_>] +codetoanalyze/cpp/quandary/basics.cpp, basics::via_field_bad2, 2, QUANDARY_TAINT_ERROR, [return from basics::template_source_>,call to basics::template_sink_>] codetoanalyze/cpp/quandary/execs.cpp, execs::callExecBad, 6, QUANDARY_TAINT_ERROR, [return from getenv,call to execl] codetoanalyze/cpp/quandary/execs.cpp, execs::callExecBad, 8, QUANDARY_TAINT_ERROR, [return from getenv,call to execl] codetoanalyze/cpp/quandary/execs.cpp, execs::callExecBad, 11, QUANDARY_TAINT_ERROR, [return from getenv,call to execl]