reporting error on unprotected access to field annotated with @GuardedBy

Reviewed By: peterogithub

Differential Revision: D3318994

fbshipit-source-id: e36b927
master
Sam Blackshear 9 years ago committed by Facebook Github Bot 7
parent f259863090
commit 36ee3730aa

@ -1 +1 @@
Subproject commit 3e46b786ad058c917a0cb23e9d26c5aaaed816be Subproject commit ba3d4b9066cc57c413581d2efe909add94b4366f

@ -59,6 +59,7 @@ ISSUE_TYPES = [
'REGISTERED_OBSERVER_BEING_DEALLOCATED', 'REGISTERED_OBSERVER_BEING_DEALLOCATED',
'ASSIGN_POINTER_WARNING', 'ASSIGN_POINTER_WARNING',
'GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL', 'GLOBAL_VARIABLE_INITIALIZED_WITH_FUNCTION_OR_METHOD_CALL',
'UNSAFE_GUARDED_BY_ACCESS',
] ]
NULL_STYLE_ISSUE_TYPES = [ NULL_STYLE_ISSUE_TYPES = [

@ -481,6 +481,7 @@ let module Node = {
/** Return [true] iff the procedure is defined, and not just declared */ /** Return [true] iff the procedure is defined, and not just declared */
let proc_desc_is_defined proc_desc => proc_desc.pd_attributes.ProcAttributes.is_defined; let proc_desc_is_defined proc_desc => proc_desc.pd_attributes.ProcAttributes.is_defined;
let proc_desc_is_java_synchroinized proc_desc => proc_desc.pd_attributes.ProcAttributes.is_java_synchronized_method;
let proc_desc_get_loc proc_desc => proc_desc.pd_attributes.ProcAttributes.loc; let proc_desc_get_loc proc_desc => proc_desc.pd_attributes.ProcAttributes.loc;
/** Return name and type of formal parameters */ /** Return name and type of formal parameters */
@ -800,6 +801,7 @@ let module Procdesc = {
let get_ret_var pdesc => Pvar.mk Ident.name_return (get_proc_name pdesc); let get_ret_var pdesc => Pvar.mk Ident.name_return (get_proc_name pdesc);
let get_start_node = Node.proc_desc_get_start_node; let get_start_node = Node.proc_desc_get_start_node;
let is_defined = Node.proc_desc_is_defined; let is_defined = Node.proc_desc_is_defined;
let is_java_synchronized = Node.proc_desc_is_java_synchroinized;
let iter_nodes = Node.proc_desc_iter_nodes; let iter_nodes = Node.proc_desc_iter_nodes;
let fold_calls = Node.proc_desc_fold_calls; let fold_calls = Node.proc_desc_fold_calls;
let iter_calls = Node.proc_desc_iter_calls; let iter_calls = Node.proc_desc_iter_calls;

@ -87,6 +87,9 @@ let module Procdesc: {
/** Return [true] iff the procedure is defined, and not just declared */ /** Return [true] iff the procedure is defined, and not just declared */
let is_defined: t => bool; let is_defined: t => bool;
/** Return [true] if the procedure signature has the Java synchronized keyword */
let is_java_synchronized: t => bool;
/** iterate over all the nodes of a procedure */ /** iterate over all the nodes of a procedure */
let iter_nodes: (node => unit) => t => unit; let iter_nodes: (node => unit) => t => unit;

@ -82,6 +82,7 @@ exception Tainted_value_reaching_sensitive_function of Localise.error_desc * L.m
exception Unary_minus_applied_to_unsigned_expression of Localise.error_desc * L.ml_loc exception Unary_minus_applied_to_unsigned_expression of Localise.error_desc * L.ml_loc
exception Uninitialized_value of Localise.error_desc * L.ml_loc exception Uninitialized_value of Localise.error_desc * L.ml_loc
exception Unknown_proc exception Unknown_proc
exception Unsafe_guarded_by_access of Localise.error_desc * L.ml_loc
exception Use_after_free of Localise.error_desc * L.ml_loc exception Use_after_free of Localise.error_desc * L.ml_loc
exception Wrong_argument_number of L.ml_loc exception Wrong_argument_number of L.ml_loc
@ -287,6 +288,9 @@ let recognize_exception exn =
| Unknown_proc -> | Unknown_proc ->
(Localise.from_string "Unknown_proc", (Localise.from_string "Unknown_proc",
Localise.no_desc, None, Exn_developer, Low, None, Nocat) Localise.no_desc, None, Exn_developer, Low, None, Nocat)
| Unsafe_guarded_by_access (desc, ml_loc) ->
(Localise.unsafe_guarded_by_access,
desc, Some ml_loc, Exn_user, High, None, Prover)
| Use_after_free (desc, ml_loc) -> | Use_after_free (desc, ml_loc) ->
(Localise.use_after_free, (Localise.use_after_free,
desc, Some ml_loc, Exn_user, High, None, Prover) desc, Some ml_loc, Exn_user, High, None, Prover)

@ -82,6 +82,7 @@ exception Tainted_value_reaching_sensitive_function of Localise.error_desc * Log
exception Unary_minus_applied_to_unsigned_expression of Localise.error_desc * Logging.ml_loc exception Unary_minus_applied_to_unsigned_expression of Localise.error_desc * Logging.ml_loc
exception Uninitialized_value of Localise.error_desc * Logging.ml_loc exception Uninitialized_value of Localise.error_desc * Logging.ml_loc
exception Unknown_proc exception Unknown_proc
exception Unsafe_guarded_by_access of Localise.error_desc * Logging.ml_loc
exception Use_after_free of Localise.error_desc * Logging.ml_loc exception Use_after_free of Localise.error_desc * Logging.ml_loc
exception Wrong_argument_number of Logging.ml_loc exception Wrong_argument_number of Logging.ml_loc

@ -70,6 +70,7 @@ let skip_pointer_dereference = "SKIP_POINTER_DEREFERENCE"
let stack_variable_address_escape = "STACK_VARIABLE_ADDRESS_ESCAPE" let stack_variable_address_escape = "STACK_VARIABLE_ADDRESS_ESCAPE"
let tainted_value_reaching_sensitive_function = "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION" let tainted_value_reaching_sensitive_function = "TAINTED_VALUE_REACHING_SENSITIVE_FUNCTION"
let unary_minus_applied_to_unsigned_expression = "UNARY_MINUS_APPLIED_TO_UNSIGNED_EXPRESSION" let unary_minus_applied_to_unsigned_expression = "UNARY_MINUS_APPLIED_TO_UNSIGNED_EXPRESSION"
let unsafe_guarded_by_access = "UNSAFE_GUARDED_BY_ACCESS"
let uninitialized_value = "UNINITIALIZED_VALUE" let uninitialized_value = "UNINITIALIZED_VALUE"
let use_after_free = "USE_AFTER_FREE" let use_after_free = "USE_AFTER_FREE"
@ -432,6 +433,23 @@ let desc_context_leak pname context_typ fieldname leak_path : error_desc =
"Context " ^ context_str ^ "may leak during method " ^ pname_str ^ ":\n" in "Context " ^ context_str ^ "may leak during method " ^ pname_str ^ ":\n" in
{ no_desc with descriptions = [preamble; leak_root; path_str] } { no_desc with descriptions = [preamble; leak_root; path_str] }
let desc_unsafe_guarded_by_access pname accessed_fld guarded_by_str loc =
let line_info = at_line (Tags.create ()) loc in
let accessed_fld_str = Ident.fieldname_to_string accessed_fld in
let annot_str = Printf.sprintf "`@GuardedBy(\"%s\")`" guarded_by_str in
let msg =
Printf.sprintf
"The field `%s` is annotated with %s, but the lock `%s` is not held during the access to the field `%s`. Consider wrapping the access in a `synchronized(%s)` block or annotating %s with %s"
accessed_fld_str
annot_str
guarded_by_str
line_info
guarded_by_str
(Procname.to_string pname)
annot_str in
{ no_desc with descriptions = [msg]; }
let desc_fragment_retains_view fragment_typ fieldname fld_typ pname : error_desc = let desc_fragment_retains_view fragment_typ fieldname fld_typ pname : error_desc =
(* TODO: try advice *) (* TODO: try advice *)
let problem = let problem =

@ -65,6 +65,7 @@ val return_statement_missing : t
val stack_variable_address_escape : t val stack_variable_address_escape : t
val unary_minus_applied_to_unsigned_expression : t val unary_minus_applied_to_unsigned_expression : t
val uninitialized_value : t val uninitialized_value : t
val unsafe_guarded_by_access : t
val use_after_free : t val use_after_free : t
val skip_function : t val skip_function : t
val skip_pointer_dereference : t val skip_pointer_dereference : t
@ -257,6 +258,9 @@ val desc_inherently_dangerous_function : Procname.t -> error_desc
val desc_unary_minus_applied_to_unsigned_expression : val desc_unary_minus_applied_to_unsigned_expression :
string option -> string -> Location.t -> error_desc string option -> string -> Location.t -> error_desc
val desc_unsafe_guarded_by_access :
Procname.t -> Ident.fieldname -> string -> Location.t -> error_desc
val desc_tainted_value_reaching_sensitive_function : val desc_tainted_value_reaching_sensitive_function :
Sil.taint_kind -> string -> Procname.t -> Procname.t -> Location.t -> error_desc Sil.taint_kind -> string -> Procname.t -> Procname.t -> Location.t -> error_desc

@ -600,12 +600,12 @@ let prop_iter_add_hpred_footprint_to_prop pname tenv prop (lexp, typ) inst =
let offsets_default = Sil.exp_get_offsets lexp in let offsets_default = Sil.exp_get_offsets lexp in
Prop.prop_iter_set_state iter offsets_default Prop.prop_iter_set_state iter offsets_default
(* TODO: have to call this on both reads and writes *) (** If [lexp] is an access to a field that is annotated with @GuardedBy, add constraints to [prop]
let add_guarded_by_constraints prop lexp = expressing the safety conditions for the access. Complain if these conditions cannot be met. *)
let add_guarded_by_constraints prop lexp pdesc =
let sigma = Prop.get_sigma prop in let sigma = Prop.get_sigma prop in
(** if [fld] is annotated with @GuardedBy("mLock"), return mLock *) let extract_guarded_by_str item_annot =
let get_guarded_by_fld_str fld typ = let annot_extract_guarded_by_str (annot, _) =
let extract_guarded_by_str (annot, _) =
if Annotations.annot_ends_with annot Annotations.guarded_by if Annotations.annot_ends_with annot Annotations.guarded_by
then then
match annot.Sil.parameters with match annot.Sil.parameters with
@ -613,53 +613,95 @@ let add_guarded_by_constraints prop lexp =
| _ -> None | _ -> None
else else
None in None in
IList.find_map_opt annot_extract_guarded_by_str item_annot in
(** if [fld] is annotated with @GuardedBy("mLock"), return mLock *)
let get_guarded_by_fld_str fld typ =
match Annotations.get_field_type_and_annotation fld typ with match Annotations.get_field_type_and_annotation fld typ with
| Some (_, item_annot) -> IList.find_map_opt extract_guarded_by_str item_annot | Some (_, item_annot) -> extract_guarded_by_str item_annot
| _ -> None in | _ -> None in
let guarded_by_str_is_this guarded_by_str =
guarded_by_str = "this" in
(* find A.guarded_by_fld_str |-> B and return Some B, or None if there is no such hpred *) (* find A.guarded_by_fld_str |-> B and return Some B, or None if there is no such hpred *)
let find_guarded_by_exp fld typ sigma = let find_guarded_by_exp guarded_by_str sigma =
match get_guarded_by_fld_str fld typ with let extract_guarded_by_strexp (fld, strexp) =
| Some guarded_by_fld_str -> (* this comparison needs to be somewhat fuzzy, since programmers are free to write
let extract_guarded_by_strexp (fld, strexp) = @GuardedBy("mLock"), @GuardedBy("MyClass.mLock"), or use other conventions *)
(* this comparison needs to be somewhat fuzzy, since programmers are free to write if Ident.fieldname_to_flat_string fld = guarded_by_str ||
@GuardedBy("mLock"), @GuardedBy("MyClass.mLock"), or use other conventions *) Ident.fieldname_to_string fld = guarded_by_str
if Ident.fieldname_to_flat_string fld = guarded_by_fld_str || then Some strexp
Ident.fieldname_to_string fld = guarded_by_fld_str else None in
then Some strexp IList.find_map_opt
else None in (function
IList.find_map_opt | Sil.Hpointsto (_, Estruct (flds, _), _) ->
(function IList.find_map_opt extract_guarded_by_strexp flds
| Sil.Hpointsto (_, Estruct (flds, _), _) -> | Sil.Hpointsto (Lvar pvar, rhs_exp, _)
IList.find_map_opt extract_guarded_by_strexp flds when guarded_by_str_is_this guarded_by_str && Pvar.is_this pvar ->
| _ -> Some rhs_exp
None) | _ ->
sigma None)
| None -> sigma in
None in (* warn if the access to [lexp] is not protected by the [guarded_by_fld_str] lock *)
let enforce_guarded_access accessed_fld guarded_by_str prop =
(* return true if [pdesc] has an annotation that matches [guarded_by_str] *)
let proc_has_matching_annot pdesc guarded_by_str =
let proc_signature =
Annotations.get_annotated_signature (Cfg.Procdesc.get_attributes pdesc) in
let proc_annot, _ = proc_signature.Annotations.ret in
match extract_guarded_by_str proc_annot with
| Some proc_guarded_by_str ->
(* the lock is not held, but the procedure is annotated with @GuardedBy *)
proc_guarded_by_str = guarded_by_str
| None -> false in
let warn pdesc accessed_fld guarded_by_str =
let pname = Cfg.Procdesc.get_proc_name pdesc in
let loc = State.get_loc () in
let err_desc =
Localise.desc_unsafe_guarded_by_access pname accessed_fld guarded_by_str loc in
let exn = Exceptions.Unsafe_guarded_by_access (err_desc, __POS__) in
Reporting.log_error pname exn in
match find_guarded_by_exp guarded_by_str (Prop.get_sigma prop) with
| Some (Sil.Eexp (guarded_by_exp, _)) ->
let has_lock =
(* procedure is synchronized and guarded by this *)
(guarded_by_str_is_this guarded_by_str && Cfg.Procdesc.is_java_synchronized pdesc) ||
(* or the prop says we already have the lock *)
IList.exists
(function
| Sil.Alocked -> true
| _ -> false)
(Prop.get_exp_attributes prop guarded_by_exp) in
if has_lock
then
(* we have the lock; no need to add a proof obligation *)
(* TODO: materialize [fld], but don't add [fld] to the footprint. *)
prop
else
if proc_has_matching_annot pdesc guarded_by_str
then
(* procedure is annotated and holding the same lock as the field. add locked proof
obligation to the current *)
(* TODO: materialize [fld], but don't add [fld] to the footprint. *)
let locked_attr = Sil.Const (Cattribute Alocked) in
Prop.conjoin_neq ~footprint:true guarded_by_exp locked_attr prop
else
begin
(* lock is not held. warn *)
warn pdesc accessed_fld guarded_by_str;
prop
end
| _ ->
if not (proc_has_matching_annot pdesc guarded_by_str)
then
(* can't find the object the annotation refers to, and procedure is not annotated. warn *)
warn pdesc accessed_fld guarded_by_str
else ();(* TODO: add proof obligation here *)
prop in
let check_fld_locks typ prop_acc (fld, strexp) = match strexp with let check_fld_locks typ prop_acc (fld, strexp) = match strexp with
| Sil.Eexp (exp, _) when Sil.exp_equal exp lexp -> | Sil.Eexp (exp, _) when Sil.exp_equal exp lexp ->
begin begin
match find_guarded_by_exp fld typ sigma with match get_guarded_by_fld_str fld typ with
| Some (Sil.Eexp (guarded_by_exp, _)) -> | Some guarded_by_fld_str -> enforce_guarded_access fld guarded_by_fld_str prop_acc
let has_lock = | None -> prop_acc (* field not annotated; proceed as normal *)
IList.exists
(function
| Sil.Alocked -> true
| _ -> false)
(Prop.get_exp_attributes prop_acc guarded_by_exp) in
if has_lock
then
(* we have the lock; no need to add a proof obligation *)
(* TODO: materialize [fld], but don't add [fld] to the footprint. *)
prop_acc
else
(* the lock is not known to be held. add a "lock held" proof obligation *)
(* TODO: materialize [fld], but don't add [fld] to the footprint. *)
let locked_attr = Sil.Const (Cattribute Alocked) in
Prop.conjoin_neq ~footprint:true guarded_by_exp locked_attr prop_acc
| _ ->
(* field not guarded; proceed with abduction as we normally would *)
prop_acc
end end
| _ -> | _ ->
prop_acc in prop_acc in
@ -1199,7 +1241,7 @@ let rearrange ?(report_deref_errors=true) pdesc tenv lexp typ prop loc
let pname = Cfg.Procdesc.get_proc_name pdesc in let pname = Cfg.Procdesc.get_proc_name pdesc in
let prop' = let prop' =
if Config.csl_analysis && !Config.footprint && Procname.is_java pname if Config.csl_analysis && !Config.footprint && Procname.is_java pname
then add_guarded_by_constraints prop lexp then add_guarded_by_constraints prop lexp pdesc
else prop in else prop in
match Prop.prop_iter_create prop' with match Prop.prop_iter_create prop' with
| None -> | None ->

@ -729,6 +729,41 @@
"file": "codetoanalyze/java/infer/ResourceLeaks.java", "file": "codetoanalyze/java/infer/ResourceLeaks.java",
"procedure": "int ResourceLeaks.readConfigNotCloseStream(String)" "procedure": "int ResourceLeaks.readConfigNotCloseStream(String)"
}, },
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readFAfterBlockBad()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readFBad()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readFBadWrongAnnotation()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readFBadWrongLock()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readGFromCopyBad()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readHBad()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readHBadSynchronizedMethodShouldntHelp()"
},
{ {
"bug_type": "RESOURCE_LEAK", "bug_type": "RESOURCE_LEAK",
"file": "codetoanalyze/java/infer/ResourceLeaks.java", "file": "codetoanalyze/java/infer/ResourceLeaks.java",
@ -774,6 +809,11 @@
"file": "codetoanalyze/java/infer/CloseableAsResourceExample.java", "file": "codetoanalyze/java/infer/CloseableAsResourceExample.java",
"procedure": "T CloseableAsResourceExample.sourceOfNullWithResourceLeak()" "procedure": "T CloseableAsResourceExample.sourceOfNullWithResourceLeak()"
}, },
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.synchronizedMethodBad()"
},
{ {
"bug_type": "NULL_DEREFERENCE", "bug_type": "NULL_DEREFERENCE",
"file": "codetoanalyze/java/infer/NullPointerExceptions.java", "file": "codetoanalyze/java/infer/NullPointerExceptions.java",

@ -734,6 +734,41 @@
"file": "infer/tests/codetoanalyze/java/infer/ResourceLeaks.java", "file": "infer/tests/codetoanalyze/java/infer/ResourceLeaks.java",
"procedure": "int ResourceLeaks.readConfigNotCloseStream(String)" "procedure": "int ResourceLeaks.readConfigNotCloseStream(String)"
}, },
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "infer/tests/codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readFAfterBlockBad()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "infer/tests/codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readFBad()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "infer/tests/codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readFBadWrongAnnotation()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "infer/tests/codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readFBadWrongLock()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "infer/tests/codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readGFromCopyBad()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "infer/tests/codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readHBad()"
},
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "infer/tests/codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.readHBadSynchronizedMethodShouldntHelp()"
},
{ {
"bug_type": "RESOURCE_LEAK", "bug_type": "RESOURCE_LEAK",
"file": "infer/tests/codetoanalyze/java/infer/ResourceLeaks.java", "file": "infer/tests/codetoanalyze/java/infer/ResourceLeaks.java",
@ -779,6 +814,11 @@
"file": "infer/tests/codetoanalyze/java/infer/CloseableAsResourceExample.java", "file": "infer/tests/codetoanalyze/java/infer/CloseableAsResourceExample.java",
"procedure": "T CloseableAsResourceExample.sourceOfNullWithResourceLeak()" "procedure": "T CloseableAsResourceExample.sourceOfNullWithResourceLeak()"
}, },
{
"bug_type": "UNSAFE_GUARDED_BY_ACCESS",
"file": "infer/tests/codetoanalyze/java/infer/GuardedByExample.java",
"procedure": "void GuardedByExample.synchronizedMethodBad()"
},
{ {
"bug_type": "NULL_DEREFERENCE", "bug_type": "NULL_DEREFERENCE",
"file": "infer/tests/codetoanalyze/java/infer/NullPointerExceptions.java", "file": "infer/tests/codetoanalyze/java/infer/NullPointerExceptions.java",

@ -0,0 +1,103 @@
/*
* 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.infer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class GuardedByExample {
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface GuardedBy {
String value();
}
private Object mLock = new Object();
private Object mOtherLock = new Object();
@GuardedBy("mLock")
Object f = new Object();
@GuardedBy("this")
Object g = new Object();
Object mCopyOfG;
@GuardedBy("SomeLockThatDoesntExist")
Object h = new Object();
void readFBad() {
this.f.toString();
}
void readFBadWrongLock() {
synchronized (mOtherLock) {
this.f.toString(); // f is supposed to be protected by mLock
}
}
void readFAfterBlockBad() {
synchronized (mLock) {
}
this.f.toString();
}
@GuardedBy("mOtherLock")
void readFBadWrongAnnotation() {
this.f.toString();
}
@GuardedBy("mLock")
void readFOkMethodAnnotated() {
this.f.toString();
}
@GuardedBy("this")
void guardedByThisOk() {
this.g.toString();
}
synchronized void synchronizedMethodOk() {
this.g.toString();
}
void readFOkSynchronized() {
synchronized (mLock) {
this.f.toString();
}
}
synchronized void synchronizedMethodBad() {
this.f.toString(); // f is supposed to be protected by mLock, not this
}
void readGFromCopyBad() {
synchronized (this) {
mCopyOfG = g; // these are ok: access of g guarded by this
g.toString();
}
mCopyOfG.toString(); // should be an error; unprotected access to pt(g)
}
void readHBad() {
synchronized (mLock) { // h is not protected by mLock
this.h.toString();
}
}
synchronized void readHBadSynchronizedMethodShouldntHelp() {
this.h.toString(); // h is not protected by this
}
}

@ -0,0 +1,63 @@
/*
* 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.java.infer;
import static org.hamcrest.MatcherAssert.assertThat;
import static utils.matchers.ResultContainsExactly.containsExactly;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import utils.InferException;
import utils.InferResults;
public class GuardedByTest {
public static final String SOURCE_FILE =
"infer/tests/codetoanalyze/java/infer/GuardedByExample.java";
public static final String UNSAFE_GUARDED_BY_ACCESS = "UNSAFE_GUARDED_BY_ACCESS";
private static InferResults inferResults;
@BeforeClass
public static void loadResults() throws InterruptedException, IOException {
inferResults = InferResults.loadInferResults(
GuardedByTest.class,
SOURCE_FILE);
}
@Test
public void matchErrors()
throws IOException, InterruptedException, InferException {
String[] methods = {
"readFBad",
"readFBadWrongLock",
"readFAfterBlockBad",
"readFBadWrongAnnotation",
"synchronizedMethodBad",
"readGFromCopyBad",
"readHBad",
"readHBadSynchronizedMethodShouldntHelp",
};
assertThat(
"Results should contain " + UNSAFE_GUARDED_BY_ACCESS,
inferResults,
containsExactly(
UNSAFE_GUARDED_BY_ACCESS,
SOURCE_FILE,
methods
)
);
}
}
Loading…
Cancel
Save