diff --git a/infer/annotations/com/facebook/infer/annotation/IntegritySink.java b/infer/annotations/com/facebook/infer/annotation/IntegritySink.java new file mode 100644 index 000000000..ec144c6a5 --- /dev/null +++ b/infer/annotations/com/facebook/infer/annotation/IntegritySink.java @@ -0,0 +1,23 @@ +/* + * 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 com.facebook.infer.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target( + ElementType.PARAMETER // a user-controlled should not flow to this parameter + ) + +public @interface IntegritySink { +} diff --git a/infer/annotations/com/facebook/infer/annotation/IntegritySource.java b/infer/annotations/com/facebook/infer/annotation/IntegritySource.java new file mode 100644 index 000000000..9b420084e --- /dev/null +++ b/infer/annotations/com/facebook/infer/annotation/IntegritySource.java @@ -0,0 +1,25 @@ +/* + * 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 com.facebook.infer.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(value={ + ElementType.METHOD, // method returns something user-controlled + ElementType.PARAMETER, // parameter is user-controlled + ElementType.FIELD, // field is user-controlled + }) + +public @interface IntegritySource { +} diff --git a/infer/src/backend/taint.ml b/infer/src/backend/taint.ml index be08d9e7e..0f9e0d9e7 100644 --- a/infer/src/backend/taint.ml +++ b/infer/src/backend/taint.ml @@ -230,7 +230,8 @@ let attrs_opt_get_annots = function let returns_tainted callee_pname callee_attrs_opt = IList.exists (fun pname -> Procname.equal pname callee_pname) sources || let ret_annot, _ = attrs_opt_get_annots callee_attrs_opt in - Annotations.ia_is_privacy_source ret_annot + Annotations.ia_is_privacy_source ret_annot || + Annotations.ia_is_integrity_source ret_annot let find_callee methods callee_pname = try @@ -244,7 +245,9 @@ let accepts_sensitive_params callee_pname callee_attrs_opt = let _, param_annots = attrs_opt_get_annots callee_attrs_opt in let offset = if Procname.java_is_static callee_pname then 0 else 1 in IList.mapi (fun param_num attr -> (param_num + offset, attr)) param_annots - |> IList.filter (fun (_, attr) -> Annotations.ia_is_privacy_sink attr) + |> IList.filter + (fun (_, attr) -> + Annotations.ia_is_privacy_sink attr || Annotations.ia_is_integrity_sink attr) |> IList.map fst | tainted_params -> tainted_params @@ -256,6 +259,6 @@ let tainted_params callee_pname = let has_taint_annotation fieldname struct_typ = let fld_has_taint_annot (fname, _, annot) = Ident.fieldname_equal fieldname fname && - Annotations.ia_is_privacy_source annot in + (Annotations.ia_is_privacy_source annot || Annotations.ia_is_integrity_source annot) in IList.exists fld_has_taint_annot struct_typ.Sil.instance_fields || IList.exists fld_has_taint_annot struct_typ.Sil.static_fields diff --git a/infer/src/checkers/annotations.ml b/infer/src/checkers/annotations.ml index 89ddbd701..27a3b55b8 100644 --- a/infer/src/checkers/annotations.ml +++ b/infer/src/checkers/annotations.ml @@ -128,6 +128,8 @@ let ignore_allocations = "IgnoreAllocations" let suppress_warnings = "SuppressWarnings" let privacy_source = "PrivacySource" let privacy_sink = "PrivacySink" +let integrity_source = "IntegritySource" +let integrity_sink = "IntegritySink" let ia_is_nullable ia = ia_ends_with ia nullable @@ -209,6 +211,12 @@ let ia_is_privacy_source ia = let ia_is_privacy_sink ia = ia_ends_with ia privacy_sink +let ia_is_integrity_source ia = + ia_ends_with ia integrity_source + +let ia_is_integrity_sink ia = + ia_ends_with ia integrity_sink + type annotation = | Nullable | Present diff --git a/infer/src/checkers/annotations.mli b/infer/src/checkers/annotations.mli index 49ada3600..b329ad08d 100644 --- a/infer/src/checkers/annotations.mli +++ b/infer/src/checkers/annotations.mli @@ -91,6 +91,8 @@ val ia_is_ignore_allocations : Sil.item_annotation -> bool val ia_is_suppress_warnings : Sil.item_annotation -> bool val ia_is_privacy_source : Sil.item_annotation -> bool val ia_is_privacy_sink : Sil.item_annotation -> bool +val ia_is_integrity_source : Sil.item_annotation -> bool +val ia_is_integrity_sink : Sil.item_annotation -> bool val ia_iter : (Sil.annotation -> unit) -> Sil.item_annotation -> unit diff --git a/infer/tests/build_systems/expected_outputs/ant_report.json b/infer/tests/build_systems/expected_outputs/ant_report.json index 014494680..56589cd27 100644 --- a/infer/tests/build_systems/expected_outputs/ant_report.json +++ b/infer/tests/build_systems/expected_outputs/ant_report.json @@ -109,6 +109,26 @@ "file": "codetoanalyze/java/infer/TaintExample.java", "procedure": "InputStream TaintExample.taintingShouldNotPreventInference2(SSLSocketFactory)" }, + { + "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", + "file": "codetoanalyze/java/infer/TaintExample.java", + "procedure": "void TaintExample.testIntegritySinkAnnotReport(String)" + }, + { + "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", + "file": "codetoanalyze/java/infer/TaintExample.java", + "procedure": "void TaintExample.testIntegritySourceAnnot()" + }, + { + "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", + "file": "codetoanalyze/java/infer/TaintExample.java", + "procedure": "void TaintExample.testIntegritySourceInstanceFieldAnnot()" + }, + { + "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", + "file": "codetoanalyze/java/infer/TaintExample.java", + "procedure": "void TaintExample.testIntegritySourceStaticFieldAnnot()" + }, { "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", "file": "codetoanalyze/java/infer/TaintExample.java", diff --git a/infer/tests/build_systems/expected_outputs/buck_report.json b/infer/tests/build_systems/expected_outputs/buck_report.json index 07f322efa..e99cf2f9c 100644 --- a/infer/tests/build_systems/expected_outputs/buck_report.json +++ b/infer/tests/build_systems/expected_outputs/buck_report.json @@ -109,6 +109,26 @@ "file": "infer/tests/codetoanalyze/java/infer/TaintExample.java", "procedure": "InputStream TaintExample.taintingShouldNotPreventInference2(SSLSocketFactory)" }, + { + "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", + "file": "infer/tests/codetoanalyze/java/infer/TaintExample.java", + "procedure": "void TaintExample.testIntegritySinkAnnotReport(String)" + }, + { + "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", + "file": "infer/tests/codetoanalyze/java/infer/TaintExample.java", + "procedure": "void TaintExample.testIntegritySourceAnnot()" + }, + { + "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", + "file": "infer/tests/codetoanalyze/java/infer/TaintExample.java", + "procedure": "void TaintExample.testIntegritySourceInstanceFieldAnnot()" + }, + { + "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", + "file": "infer/tests/codetoanalyze/java/infer/TaintExample.java", + "procedure": "void TaintExample.testIntegritySourceStaticFieldAnnot()" + }, { "bug_type": "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION", "file": "infer/tests/codetoanalyze/java/infer/TaintExample.java", diff --git a/infer/tests/codetoanalyze/java/infer/TaintExample.java b/infer/tests/codetoanalyze/java/infer/TaintExample.java index 55017efee..6e3d11bdb 100644 --- a/infer/tests/codetoanalyze/java/infer/TaintExample.java +++ b/infer/tests/codetoanalyze/java/infer/TaintExample.java @@ -25,6 +25,8 @@ import android.content.ContentValues; import android.content.SharedPreferences; import com.facebook.infer.models.InferTaint; +import com.facebook.infer.annotation.IntegritySource; +import com.facebook.infer.annotation.IntegritySink; import com.facebook.infer.annotation.PrivacySource; import com.facebook.infer.annotation.PrivacySink; @@ -228,4 +230,38 @@ public class TaintExample { InferTaint.inferSensitiveSinkUndefined(aFieldWithoutAnnotations); // should report } + @IntegritySource + public String integritySource() { + return "source"; + } + + @IntegritySource String mIntegritySource; + + @IntegritySource String sIntegritySource; + + public void testIntegritySourceAnnot() { + InferTaint.inferSensitiveSinkUndefined(integritySource()); // should report + } + + public void testIntegritySourceInstanceFieldAnnot() { + String source = mIntegritySource; + InferTaint.inferSensitiveSinkUndefined(source); // should report + } + + public void testIntegritySourceStaticFieldAnnot() { + String source = sIntegritySource; + InferTaint.inferSensitiveSinkUndefined(source); // should report + } + + public void integritySink(@IntegritySink String s1, String s2) { + } + + void testIntegritySinkAnnotReport(String s) { + integritySink(integritySource(), s); // should report + } + + void testIntegritySinkAnnotNoReport(String s) { + integritySink(s, integritySource()); // should not report + } + } diff --git a/infer/tests/endtoend/java/infer/TaintTest.java b/infer/tests/endtoend/java/infer/TaintTest.java index cfefd4113..2dfc8cc7e 100644 --- a/infer/tests/endtoend/java/infer/TaintTest.java +++ b/infer/tests/endtoend/java/infer/TaintTest.java @@ -61,6 +61,10 @@ public class TaintTest { "testPrivacySourceInstanceFieldAnnot", "testPrivacySourceStaticFieldAnnot", "testPrivacySourceFieldAnnotPropagation", + "testIntegritySourceAnnot", + "testIntegritySourceInstanceFieldAnnot", + "testIntegritySourceStaticFieldAnnot", + "testIntegritySinkAnnotReport", }; assertThat(