diff --git a/infer/src/quandary/TaintAnalysis.ml b/infer/src/quandary/TaintAnalysis.ml index ccb8438b7..518f925aa 100644 --- a/infer/src/quandary/TaintAnalysis.ml +++ b/infer/src/quandary/TaintAnalysis.ml @@ -297,34 +297,35 @@ module Make (TaintSpec : TaintSpec.S) = struct Location.pp loc else astate - | Sil.Call (ret, Const (Cfun callee_pname), actuals, callee_loc, _) -> - let call_site = CallSite.make callee_pname callee_loc in - - let astate_with_sink = - match TraceDomain.Sink.get call_site actuals with - | [] -> astate - | sinks -> add_sinks sinks actuals astate proc_data callee_loc in - - let astate_with_source = - match TraceDomain.Source.get call_site, ret with - | Some source, Some (ret_id, ret_typ) -> - let access_tree = add_source source ret_id ret_typ astate_with_sink.access_tree in - { astate_with_sink with access_tree; } - | Some _, None -> - failwithf - "%a is marked as a source, but has no return value" Procname.pp callee_pname - | None, _ -> - astate_with_sink in - let astate_with_summary = + | Sil.Call (ret, Const (Cfun called_pname), actuals, callee_loc, call_flags) -> + let analyze_call astate_acc callee_pname = + let call_site = CallSite.make callee_pname callee_loc in + let astate_with_sink = + match TraceDomain.Sink.get call_site actuals with + | [] -> astate + | sinks -> add_sinks sinks actuals astate proc_data callee_loc in + + let astate_with_source = + match TraceDomain.Source.get call_site, ret with + | Some source, Some (ret_id, ret_typ) -> + let access_tree = add_source source ret_id ret_typ astate_with_sink.access_tree in + { astate_with_sink with access_tree; } + | Some _, None -> + failwithf + "%a is marked as a source, but has no return value" Procname.pp callee_pname + | None, _ -> + astate_with_sink in + let summary = match Summary.read_summary proc_data.tenv proc_data.pdesc callee_pname with | Some summary -> summary - | None -> TaintSpec.handle_unknown_call call_site (Option.map snd ret) in - apply_summary ret actuals summary astate_with_source proc_data call_site in + apply_summary ret actuals summary astate_with_source proc_data call_site + |> Domain.join astate_acc in - astate_with_summary + (* for each possible target of the call, apply the summary. join all results together *) + IList.fold_left analyze_call Domain.initial (called_pname :: call_flags.cf_targets) | Sil.Call _ -> failwith "Unimp: non-pname call expressions" | Sil.Nullify (pvar, _) -> @@ -436,7 +437,7 @@ module Make (TaintSpec : TaintSpec.S) = struct AccessPath.BaseMap.empty formals_with_nums in - Preanal.doit pdesc dummy_cg tenv; + Preanal.doit ~handle_dynamic_dispatch:true pdesc dummy_cg tenv; let formals = make_formal_access_paths pdesc in let proc_data = ProcData.make pdesc tenv formals in match Analyzer.compute_post proc_data with diff --git a/infer/tests/codetoanalyze/java/quandary/DynamicDispatch.java b/infer/tests/codetoanalyze/java/quandary/DynamicDispatch.java new file mode 100644 index 000000000..d0ef71125 --- /dev/null +++ b/infer/tests/codetoanalyze/java/quandary/DynamicDispatch.java @@ -0,0 +1,163 @@ +/* + * 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 codetoanalyze.java.quandary; + +import com.facebook.infer.builtins.InferTaint; + +public class DynamicDispatch { + + static interface Interface { + public Object returnSource(); + public void callSink(Object o); + public Object propagate(Object o); + } + + static class BadInterfaceImpl1 implements Interface { + @Override + public Object returnSource() { + return InferTaint.inferSecretSource(); + } + + @Override + public void callSink(Object o) { + InferTaint.inferSensitiveSink(o); + } + + @Override + public Object propagate(Object o) { + return o; + } + } + + static class BadInterfaceImpl2 implements Interface { + @Override + public Object returnSource() { + return InferTaint.inferSecretSource(); + } + + @Override + public void callSink(Object o) { + InferTaint.inferSensitiveSink(o); + } + + @Override + public Object propagate(Object o) { + return o; + } + } + + static class OkInterfaceImpl implements Interface { + @Override + public Object returnSource() { + return null; + } + + @Override + public void callSink(Object o) { + } + + @Override + public Object propagate(Object o) { + return null; + } + } + + /** interface tests. for all of these, we should see a warning for both BadInterfaceImpl1 and + BadInterfaceImpl2, but not OkInterfaceImpl */ + + static void returnSourceViaInterfaceBad(Interface i) { + Object source = i.returnSource(); + InferTaint.inferSensitiveSink(source); + } + + static void callSinkViaInterfaceBad(Interface i) { + Object source = InferTaint.inferSecretSource(); + i.callSink(source); + } + + static void propagateViaInterfaceBad(Interface i) { + Object source = InferTaint.inferSecretSource(); + Object launderedSource = i.propagate(source); + InferTaint.inferSensitiveSink(launderedSource); + } + + static void interfaceOk() { + Interface i = new OkInterfaceImpl(); + Object source1 = i.returnSource(); + InferTaint.inferSensitiveSink(source1); + + Object source2 = InferTaint.inferSecretSource(); + i.callSink(source2); + + Object launderedSource = i.propagate(source2); + InferTaint.inferSensitiveSink(launderedSource); + } + + static class Supertype { + public Object returnSource() { + return null; + } + + public void callSink(Object o) { + } + + public Object propagate(Object o) { + return null; + } + } + + static class BadSubtype extends Supertype { + @Override + public Object returnSource() { + return InferTaint.inferSecretSource(); + } + + @Override + public void callSink(Object o) { + InferTaint.inferSensitiveSink(o); + } + + @Override + public Object propagate(Object o) { + return o; + } + } + + static void returnSourceViaSubtypeBad(Supertype s) { + Object source = s.returnSource(); + InferTaint.inferSensitiveSink(source); + } + + static void callSinkViaSubtypeBad(Supertype s) { + Object source = InferTaint.inferSecretSource(); + s.callSink(source); + } + + static void propagateViaSubtypeBad(Supertype s) { + Object source = InferTaint.inferSecretSource(); + Object launderedSource = s.propagate(source); + InferTaint.inferSensitiveSink(launderedSource); + } + + // need to look and see if we know the concrete type of the receiver to get this one + static void FP_propagateViaConcreteTypeOk() { + Supertype s = new Supertype(); + + Object source1 = s.returnSource(); + InferTaint.inferSensitiveSink(source1); + + Object source2 = InferTaint.inferSecretSource(); + s.callSink(source2); + + Object launderedSource = s.propagate(source2); + InferTaint.inferSensitiveSink(launderedSource); + } + +} diff --git a/infer/tests/codetoanalyze/java/quandary/Makefile b/infer/tests/codetoanalyze/java/quandary/Makefile index e5c4d8a88..d875c8dec 100644 --- a/infer/tests/codetoanalyze/java/quandary/Makefile +++ b/infer/tests/codetoanalyze/java/quandary/Makefile @@ -13,6 +13,7 @@ INFERPRINT_OPTIONS = --issues-txt FILES = \ Arrays.java \ Basics.java \ + DynamicDispatch.java \ Exceptions.java \ Fields.java \ Intents.java \ diff --git a/infer/tests/codetoanalyze/java/quandary/issues.exp b/infer/tests/codetoanalyze/java/quandary/issues.exp index 2d5c35d2d..b08ff761d 100644 --- a/infer/tests/codetoanalyze/java/quandary/issues.exp +++ b/infer/tests/codetoanalyze/java/quandary/issues.exp @@ -22,6 +22,19 @@ Basics.java:142: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.infe Basics.java:153: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 150]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 153]) via { } Basics.java:196: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 193]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 196]) via { } Basics.java:205: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 201]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 205]) via { } +DynamicDispatch.java:77: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 25]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 77]) via { Object DynamicDispatch$BadInterfaceImpl1.returnSource() at [line 76], Object DynamicDispatch$BadInterfaceImpl2.returnSource() at [line 76] } +DynamicDispatch.java:77: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 42]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 77]) via { Object DynamicDispatch$BadInterfaceImpl1.returnSource() at [line 76], Object DynamicDispatch$BadInterfaceImpl2.returnSource() at [line 76] } +DynamicDispatch.java:82: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 81]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 30]) via { void DynamicDispatch$BadInterfaceImpl1.callSink(Object) at [line 82] } +DynamicDispatch.java:82: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 81]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 47]) via { void DynamicDispatch$BadInterfaceImpl2.callSink(Object) at [line 82] } +DynamicDispatch.java:88: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 86]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 88]) via { Object DynamicDispatch$BadInterfaceImpl1.propagate(Object) at [line 87], Object DynamicDispatch$BadInterfaceImpl2.propagate(Object) at [line 87] } +DynamicDispatch.java:135: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 119]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 135]) via { Object DynamicDispatch$BadSubtype.returnSource() at [line 134] } +DynamicDispatch.java:140: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 139]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 124]) via { void DynamicDispatch$BadSubtype.callSink(Object) at [line 140] } +DynamicDispatch.java:146: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 144]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 146]) via { Object DynamicDispatch$BadSubtype.propagate(Object) at [line 145] } +DynamicDispatch.java:154: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 119]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 154]) via { Object DynamicDispatch$BadSubtype.returnSource() at [line 153] } +DynamicDispatch.java:157: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 156]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 124]) via { void DynamicDispatch$BadSubtype.callSink(Object) at [line 157] } +DynamicDispatch.java:159: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 156]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 124]) via { void DynamicDispatch$BadSubtype.callSink(Object) at [line 157], Object DynamicDispatch$BadSubtype.propagate(Object) at [line 159] } +DynamicDispatch.java:160: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 156]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 124]) via { void DynamicDispatch$BadSubtype.callSink(Object) at [line 157], Object DynamicDispatch$BadSubtype.propagate(Object) at [line 159] } +DynamicDispatch.java:160: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 156]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 160]) via { void DynamicDispatch$BadSubtype.callSink(Object) at [line 157], Object DynamicDispatch$BadSubtype.propagate(Object) at [line 159] } Exceptions.java:23: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 19]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 23]) via { } Exceptions.java:33: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 30]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 33]) via { } Exceptions.java:44: ERROR: QUANDARY_TAINT_ERROR Error: Other(Object InferTaint.inferSecretSource() at [line 38]) -> Other(void InferTaint.inferSensitiveSink(Object) at [line 44]) via { }