[quandary] Allow sources to have multiple taints

Reviewed By: ngorogiannis

Differential Revision: D13163833

fbshipit-source-id: 89c27acea
master
Mehdi Bouaziz 6 years ago committed by Facebook Github Bot
parent 8240ca4430
commit 5b3bca5562

@ -16,7 +16,7 @@ let all_formals_untainted pdesc =
module type Kind = sig
include TraceElem.Kind
val get : Typ.Procname.t -> HilExp.t list -> Tenv.t -> (t * int option) option
val get : Typ.Procname.t -> HilExp.t list -> Tenv.t -> (t * int option) list
val get_tainted_formals : Procdesc.t -> Tenv.t -> (Mangled.t * Typ.t * t option) list
end
@ -26,7 +26,7 @@ module type S = sig
type spec = {source: t; index: int option}
val get : CallSite.t -> HilExp.t list -> Tenv.t -> spec option
val get : CallSite.t -> HilExp.t list -> Tenv.t -> spec list
val get_tainted_formals : Procdesc.t -> Tenv.t -> (Mangled.t * Typ.t * t option) list
end
@ -45,12 +45,10 @@ module Make (Kind : Kind) = struct
let make ?indexes:_ kind site = {site; kind}
let get site actuals tenv =
match Kind.get (CallSite.pname site) actuals tenv with
| Some (kind, index) ->
let source = make kind site in
Some {source; index}
| None ->
None
Kind.get (CallSite.pname site) actuals tenv
|> List.rev_map ~f:(fun (kind, index) ->
let source = make kind site in
{source; index} )
let get_tainted_formals pdesc tenv =
@ -87,7 +85,7 @@ module Dummy = struct
let pp _ () = ()
let get _ _ _ = None
let get _ _ _ = []
let get_tainted_formals pdesc _ =
List.map ~f:(fun (name, typ) -> (name, typ, None)) (Procdesc.get_formals pdesc)

@ -13,7 +13,7 @@ val all_formals_untainted : Procdesc.t -> (Mangled.t * Typ.t * 'a option) list
module type Kind = sig
include TraceElem.Kind
val get : Typ.Procname.t -> HilExp.t list -> Tenv.t -> (t * int option) option
val get : Typ.Procname.t -> HilExp.t list -> Tenv.t -> (t * int option) list
(** return Some (kind) if the procedure with the given actuals is a taint source, None otherwise *)
val get_tainted_formals : Procdesc.t -> Tenv.t -> (Mangled.t * Typ.t * t option) list
@ -28,7 +28,7 @@ module type S = sig
{ source: t (** type of the returned source *)
; index: int option (** index of the returned source if Some; return value if None *) }
val get : CallSite.t -> HilExp.t list -> Tenv.t -> spec option
val get : CallSite.t -> HilExp.t list -> Tenv.t -> spec list
(** return Some (taint spec) if the call site with the given actuals is a taint source, None otherwise *)
val get_tainted_formals : Procdesc.t -> Tenv.t -> (Mangled.t * Typ.t * t option) list

@ -59,13 +59,11 @@ module SourceKind = struct
(* return Some(source kind) if [procedure_name] is in the list of externally specified sources *)
let get_external_source qualified_pname =
let return = None in
List.find_map
~f:(fun (qualifiers, kind, index) ->
List.filter_map external_sources ~f:(fun (qualifiers, kind, index) ->
if QualifiedCppName.Match.match_qualifiers qualifiers qualified_pname then
let source_index = try Some (int_of_string index) with Failure _ -> return in
Some (of_string kind, source_index)
else None )
external_sources
let get pname actuals tenv =
@ -80,7 +78,7 @@ module SourceKind = struct
with
| ( ["std"; ("basic_istream" | "basic_iostream")]
, ("getline" | "read" | "readsome" | "operator>>") ) ->
Some (ReadFile, Some 1)
[(ReadFile, Some 1)]
| _ ->
get_external_source qualified_pname )
| Typ.Procname.C _ when Typ.Procname.equal pname BuiltinDecl.__global_access -> (
@ -109,18 +107,18 @@ module SourceKind = struct
| None ->
Typ.void_star.desc
in
Some (CommandLineFlag (global_pvar, typ_desc), None)
else None
[(CommandLineFlag (global_pvar, typ_desc), None)]
else []
| _ ->
None )
[] )
| Typ.Procname.C _ -> (
match Typ.Procname.to_string pname with
| "getenv" ->
Some (EnvironmentVariable, return)
[(EnvironmentVariable, return)]
| _ ->
get_external_source (Typ.Procname.get_qualifiers pname) )
| Typ.Procname.Block _ ->
None
[]
| pname ->
L.(die InternalError) "Non-C++ procname %a in C++ analysis" Typ.Procname.pp pname

@ -64,11 +64,12 @@ module SourceKind = struct
let get_external_source class_name method_name =
(* check the list of externally specified sources *)
let procedure = class_name ^ "." ^ method_name in
List.find_map
~f:(fun (procedure_regex, kind) ->
if Str.string_match procedure_regex procedure 0 then Some (of_string kind, return)
else None )
external_sources
let sources =
List.filter_map external_sources ~f:(fun (procedure_regex, kind) ->
if Str.string_match procedure_regex procedure 0 then Some (of_string kind, return)
else None )
in
Option.some_if (not (List.is_empty sources)) sources
in
match pname with
| Typ.Procname.Java pname ->
@ -76,46 +77,47 @@ module SourceKind = struct
let taint_matching_supertype typename =
match (Typ.Name.name typename, method_name) with
| "android.app.Activity", "getIntent" ->
Some (Intent, return)
Some [(Intent, return)]
| "android.content.Intent", "<init>"
when actual_has_type 2 "android.net.Uri" actuals tenv ->
(* taint the [this] parameter passed to the constructor *)
Some (IntentFromURI, Some 0)
Some [(IntentFromURI, Some 0)]
| ( "android.content.Intent"
, ( "parseUri"
| "setData"
| "setDataAndNormalize"
| "setDataAndType"
| "setDataAndTypeAndNormalize" ) ) ->
Some (IntentFromURI, return)
Some [(IntentFromURI, return)]
| "android.content.Intent", "getStringExtra" ->
Some (Intent, return)
Some [(Intent, return)]
| "android.content.SharedPreferences", "getString" ->
Some (PrivateData, return)
Some [(PrivateData, return)]
| ( ("android.content.ClipboardManager" | "android.text.ClipboardManager")
, ("getPrimaryClip" | "getText") ) ->
Some (UserControlledString, return)
Some [(UserControlledString, return)]
| ( "android.location.Location"
, ("getAltitude" | "getBearing" | "getLatitude" | "getLongitude" | "getSpeed") ) ->
Some (PrivateData, return)
Some [(PrivateData, return)]
| ( "android.telephony.TelephonyManager"
, ( "getDeviceId"
| "getLine1Number"
| "getSimSerialNumber"
| "getSubscriberId"
| "getVoiceMailNumber" ) ) ->
Some (PrivateData, return)
Some [(PrivateData, return)]
| "android.webkit.WebResourceRequest", "getUrl" ->
Some (UserControlledURI, return)
Some [(UserControlledURI, return)]
| "android.widget.EditText", "getText" ->
Some (UserControlledString, return)
Some [(UserControlledString, return)]
| "com.facebook.infer.builtins.InferTaint", "inferSecretSource" ->
Some (Other, return)
Some [(Other, return)]
| class_name, method_name ->
get_external_source class_name method_name
in
PatternMatch.supertype_find_map_opt tenv taint_matching_supertype
(Typ.Name.Java.from_string (Typ.Procname.Java.get_class_name pname))
|> Option.value ~default:[]
| Typ.Procname.C _ when Typ.Procname.equal pname BuiltinDecl.__global_access -> (
(* accessed global will be passed to us as the only parameter *)
match List.map actuals ~f:HilExp.ignore_cast with
@ -126,14 +128,14 @@ module SourceKind = struct
(* checking substring instead of prefix because we expect field names like
com.myapp.R$drawable.whatever *)
if String.is_substring ~substring:AndroidFramework.drawable_prefix pvar_string then
Some (DrawableResource pvar, None)
else None
[(DrawableResource pvar, None)]
else []
| _ ->
None )
[] )
| _ ->
None )
[] )
| pname when BuiltinDecl.is_declared pname ->
None
[]
| pname ->
L.(die InternalError) "Non-Java procname %a in Java analysis" Typ.Procname.pp pname

@ -482,12 +482,12 @@ module Make (TaintSpecification : TaintSpec.S) = struct
let var, _ = AccessExpression.get_base access_expr in
if Var.is_global var then
let dummy_call_site = CallSite.make BuiltinDecl.__global_access loc in
match
let sources =
TraceDomain.Source.get dummy_call_site
[HilExp.AccessExpression access_expr]
proc_data.tenv
with
| Some {TraceDomain.Source.source} ->
in
List.fold sources ~init:astate ~f:(fun astate {TraceDomain.Source.source} ->
let access_path =
AccessPath.Abs.Exact (AccessExpression.to_access_path access_expr)
in
@ -497,9 +497,7 @@ module Make (TaintSpecification : TaintSpec.S) = struct
in
TaintDomain.add_node access_path
(TraceDomain.add_source source trace, subtree)
astate
| None ->
astate
astate )
else astate
in
let rec add_sources_sinks_for_exp exp loc astate =
@ -681,26 +679,23 @@ module Make (TaintSpecification : TaintSpec.S) = struct
| None ->
astate
in
let source = TraceDomain.Source.get call_site actuals proc_data.tenv in
let astate_with_source =
match source with
| Some {TraceDomain.Source.source; index= None} ->
Option.value_map
~f:(fun ret_base -> add_return_source source ret_base astate_with_sink)
~default:astate_with_sink dummy_ret_opt
| Some {TraceDomain.Source.source; index= Some index} ->
add_actual_source source index actuals astate_with_sink proc_data
| None ->
astate_with_sink
in
let astate_with_summary =
if Option.is_some source then
(* don't use a summary for a procedure that is a direct source *)
astate_with_source
else
let sources = TraceDomain.Source.get call_site actuals proc_data.tenv in
match sources with
| _ :: _ ->
(* don't use a summary for a procedure that is a direct source *)
List.fold sources ~init:astate_with_sink
~f:(fun astate {TraceDomain.Source.source; index} ->
match index with
| None ->
Option.value_map dummy_ret_opt ~default:astate ~f:(fun ret_base ->
add_return_source source ret_base astate )
| Some index ->
add_actual_source source index actuals astate_with_sink proc_data )
| [] -> (
match Payload.read proc_data.pdesc callee_pname with
| None ->
handle_unknown_call callee_pname astate_with_source
handle_unknown_call callee_pname astate_with_sink
| Some summary -> (
let ret_typ = snd ret_ap in
let access_tree = TaintSpecification.of_summary_access_tree summary in
@ -709,10 +704,10 @@ module Make (TaintSpecification : TaintSpec.S) = struct
access_tree
with
| Some model ->
handle_model callee_pname astate_with_source model
handle_model callee_pname astate_with_sink model
| None ->
apply_summary dummy_ret_opt actuals access_tree astate_with_source
proc_data call_site )
apply_summary dummy_ret_opt actuals access_tree astate_with_sink proc_data
call_site ) )
in
let astate_with_sanitizer =
match dummy_ret_opt with

@ -20,8 +20,8 @@ module MockTrace = Trace.Make (struct
let get pname _ _ =
if String.is_prefix ~prefix:"SOURCE" (Typ.Procname.to_string pname) then
Some (CallSite.make pname Location.dummy, None)
else None
[(CallSite.make pname Location.dummy, None)]
else []
let get_tainted_formals _ _ = []

Loading…
Cancel
Save