[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 module type Kind = sig
include TraceElem.Kind 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 val get_tainted_formals : Procdesc.t -> Tenv.t -> (Mangled.t * Typ.t * t option) list
end end
@ -26,7 +26,7 @@ module type S = sig
type spec = {source: t; index: int option} 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 val get_tainted_formals : Procdesc.t -> Tenv.t -> (Mangled.t * Typ.t * t option) list
end end
@ -45,12 +45,10 @@ module Make (Kind : Kind) = struct
let make ?indexes:_ kind site = {site; kind} let make ?indexes:_ kind site = {site; kind}
let get site actuals tenv = let get site actuals tenv =
match Kind.get (CallSite.pname site) actuals tenv with Kind.get (CallSite.pname site) actuals tenv
| Some (kind, index) -> |> List.rev_map ~f:(fun (kind, index) ->
let source = make kind site in let source = make kind site in
Some {source; index} {source; index} )
| None ->
None
let get_tainted_formals pdesc tenv = let get_tainted_formals pdesc tenv =
@ -87,7 +85,7 @@ module Dummy = struct
let pp _ () = () let pp _ () = ()
let get _ _ _ = None let get _ _ _ = []
let get_tainted_formals pdesc _ = let get_tainted_formals pdesc _ =
List.map ~f:(fun (name, typ) -> (name, typ, None)) (Procdesc.get_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 module type Kind = sig
include TraceElem.Kind 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 *) (** 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 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 *) { source: t (** type of the returned source *)
; index: int option (** index of the returned source if Some; return value if None *) } ; 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 *) (** 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 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 *) (* return Some(source kind) if [procedure_name] is in the list of externally specified sources *)
let get_external_source qualified_pname = let get_external_source qualified_pname =
let return = None in let return = None in
List.find_map List.filter_map external_sources ~f:(fun (qualifiers, kind, index) ->
~f:(fun (qualifiers, kind, index) ->
if QualifiedCppName.Match.match_qualifiers qualifiers qualified_pname then if QualifiedCppName.Match.match_qualifiers qualifiers qualified_pname then
let source_index = try Some (int_of_string index) with Failure _ -> return in let source_index = try Some (int_of_string index) with Failure _ -> return in
Some (of_string kind, source_index) Some (of_string kind, source_index)
else None ) else None )
external_sources
let get pname actuals tenv = let get pname actuals tenv =
@ -80,7 +78,7 @@ module SourceKind = struct
with with
| ( ["std"; ("basic_istream" | "basic_iostream")] | ( ["std"; ("basic_istream" | "basic_iostream")]
, ("getline" | "read" | "readsome" | "operator>>") ) -> , ("getline" | "read" | "readsome" | "operator>>") ) ->
Some (ReadFile, Some 1) [(ReadFile, Some 1)]
| _ -> | _ ->
get_external_source qualified_pname ) get_external_source qualified_pname )
| Typ.Procname.C _ when Typ.Procname.equal pname BuiltinDecl.__global_access -> ( | Typ.Procname.C _ when Typ.Procname.equal pname BuiltinDecl.__global_access -> (
@ -109,18 +107,18 @@ module SourceKind = struct
| None -> | None ->
Typ.void_star.desc Typ.void_star.desc
in in
Some (CommandLineFlag (global_pvar, typ_desc), None) [(CommandLineFlag (global_pvar, typ_desc), None)]
else None else []
| _ -> | _ ->
None ) [] )
| Typ.Procname.C _ -> ( | Typ.Procname.C _ -> (
match Typ.Procname.to_string pname with match Typ.Procname.to_string pname with
| "getenv" -> | "getenv" ->
Some (EnvironmentVariable, return) [(EnvironmentVariable, return)]
| _ -> | _ ->
get_external_source (Typ.Procname.get_qualifiers pname) ) get_external_source (Typ.Procname.get_qualifiers pname) )
| Typ.Procname.Block _ -> | Typ.Procname.Block _ ->
None []
| pname -> | pname ->
L.(die InternalError) "Non-C++ procname %a in C++ analysis" Typ.Procname.pp 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 = let get_external_source class_name method_name =
(* check the list of externally specified sources *) (* check the list of externally specified sources *)
let procedure = class_name ^ "." ^ method_name in let procedure = class_name ^ "." ^ method_name in
List.find_map let sources =
~f:(fun (procedure_regex, kind) -> List.filter_map external_sources ~f:(fun (procedure_regex, kind) ->
if Str.string_match procedure_regex procedure 0 then Some (of_string kind, return) if Str.string_match procedure_regex procedure 0 then Some (of_string kind, return)
else None ) else None )
external_sources in
Option.some_if (not (List.is_empty sources)) sources
in in
match pname with match pname with
| Typ.Procname.Java pname -> | Typ.Procname.Java pname ->
@ -76,46 +77,47 @@ module SourceKind = struct
let taint_matching_supertype typename = let taint_matching_supertype typename =
match (Typ.Name.name typename, method_name) with match (Typ.Name.name typename, method_name) with
| "android.app.Activity", "getIntent" -> | "android.app.Activity", "getIntent" ->
Some (Intent, return) Some [(Intent, return)]
| "android.content.Intent", "<init>" | "android.content.Intent", "<init>"
when actual_has_type 2 "android.net.Uri" actuals tenv -> when actual_has_type 2 "android.net.Uri" actuals tenv ->
(* taint the [this] parameter passed to the constructor *) (* taint the [this] parameter passed to the constructor *)
Some (IntentFromURI, Some 0) Some [(IntentFromURI, Some 0)]
| ( "android.content.Intent" | ( "android.content.Intent"
, ( "parseUri" , ( "parseUri"
| "setData" | "setData"
| "setDataAndNormalize" | "setDataAndNormalize"
| "setDataAndType" | "setDataAndType"
| "setDataAndTypeAndNormalize" ) ) -> | "setDataAndTypeAndNormalize" ) ) ->
Some (IntentFromURI, return) Some [(IntentFromURI, return)]
| "android.content.Intent", "getStringExtra" -> | "android.content.Intent", "getStringExtra" ->
Some (Intent, return) Some [(Intent, return)]
| "android.content.SharedPreferences", "getString" -> | "android.content.SharedPreferences", "getString" ->
Some (PrivateData, return) Some [(PrivateData, return)]
| ( ("android.content.ClipboardManager" | "android.text.ClipboardManager") | ( ("android.content.ClipboardManager" | "android.text.ClipboardManager")
, ("getPrimaryClip" | "getText") ) -> , ("getPrimaryClip" | "getText") ) ->
Some (UserControlledString, return) Some [(UserControlledString, return)]
| ( "android.location.Location" | ( "android.location.Location"
, ("getAltitude" | "getBearing" | "getLatitude" | "getLongitude" | "getSpeed") ) -> , ("getAltitude" | "getBearing" | "getLatitude" | "getLongitude" | "getSpeed") ) ->
Some (PrivateData, return) Some [(PrivateData, return)]
| ( "android.telephony.TelephonyManager" | ( "android.telephony.TelephonyManager"
, ( "getDeviceId" , ( "getDeviceId"
| "getLine1Number" | "getLine1Number"
| "getSimSerialNumber" | "getSimSerialNumber"
| "getSubscriberId" | "getSubscriberId"
| "getVoiceMailNumber" ) ) -> | "getVoiceMailNumber" ) ) ->
Some (PrivateData, return) Some [(PrivateData, return)]
| "android.webkit.WebResourceRequest", "getUrl" -> | "android.webkit.WebResourceRequest", "getUrl" ->
Some (UserControlledURI, return) Some [(UserControlledURI, return)]
| "android.widget.EditText", "getText" -> | "android.widget.EditText", "getText" ->
Some (UserControlledString, return) Some [(UserControlledString, return)]
| "com.facebook.infer.builtins.InferTaint", "inferSecretSource" -> | "com.facebook.infer.builtins.InferTaint", "inferSecretSource" ->
Some (Other, return) Some [(Other, return)]
| class_name, method_name -> | class_name, method_name ->
get_external_source class_name method_name get_external_source class_name method_name
in in
PatternMatch.supertype_find_map_opt tenv taint_matching_supertype PatternMatch.supertype_find_map_opt tenv taint_matching_supertype
(Typ.Name.Java.from_string (Typ.Procname.Java.get_class_name pname)) (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 -> ( | Typ.Procname.C _ when Typ.Procname.equal pname BuiltinDecl.__global_access -> (
(* accessed global will be passed to us as the only parameter *) (* accessed global will be passed to us as the only parameter *)
match List.map actuals ~f:HilExp.ignore_cast with 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 (* checking substring instead of prefix because we expect field names like
com.myapp.R$drawable.whatever *) com.myapp.R$drawable.whatever *)
if String.is_substring ~substring:AndroidFramework.drawable_prefix pvar_string then if String.is_substring ~substring:AndroidFramework.drawable_prefix pvar_string then
Some (DrawableResource pvar, None) [(DrawableResource pvar, None)]
else None else []
| _ -> | _ ->
None ) [] )
| _ -> | _ ->
None ) [] )
| pname when BuiltinDecl.is_declared pname -> | pname when BuiltinDecl.is_declared pname ->
None []
| pname -> | pname ->
L.(die InternalError) "Non-Java procname %a in Java analysis" Typ.Procname.pp 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 let var, _ = AccessExpression.get_base access_expr in
if Var.is_global var then if Var.is_global var then
let dummy_call_site = CallSite.make BuiltinDecl.__global_access loc in let dummy_call_site = CallSite.make BuiltinDecl.__global_access loc in
match let sources =
TraceDomain.Source.get dummy_call_site TraceDomain.Source.get dummy_call_site
[HilExp.AccessExpression access_expr] [HilExp.AccessExpression access_expr]
proc_data.tenv proc_data.tenv
with in
| Some {TraceDomain.Source.source} -> List.fold sources ~init:astate ~f:(fun astate {TraceDomain.Source.source} ->
let access_path = let access_path =
AccessPath.Abs.Exact (AccessExpression.to_access_path access_expr) AccessPath.Abs.Exact (AccessExpression.to_access_path access_expr)
in in
@ -497,9 +497,7 @@ module Make (TaintSpecification : TaintSpec.S) = struct
in in
TaintDomain.add_node access_path TaintDomain.add_node access_path
(TraceDomain.add_source source trace, subtree) (TraceDomain.add_source source trace, subtree)
astate astate )
| None ->
astate
else astate else astate
in in
let rec add_sources_sinks_for_exp exp loc astate = let rec add_sources_sinks_for_exp exp loc astate =
@ -681,26 +679,23 @@ module Make (TaintSpecification : TaintSpec.S) = struct
| None -> | None ->
astate astate
in 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 = let astate_with_summary =
if Option.is_some source then let sources = TraceDomain.Source.get call_site actuals proc_data.tenv in
(* don't use a summary for a procedure that is a direct source *) match sources with
astate_with_source | _ :: _ ->
else (* 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 match Payload.read proc_data.pdesc callee_pname with
| None -> | None ->
handle_unknown_call callee_pname astate_with_source handle_unknown_call callee_pname astate_with_sink
| Some summary -> ( | Some summary -> (
let ret_typ = snd ret_ap in let ret_typ = snd ret_ap in
let access_tree = TaintSpecification.of_summary_access_tree summary in let access_tree = TaintSpecification.of_summary_access_tree summary in
@ -709,10 +704,10 @@ module Make (TaintSpecification : TaintSpec.S) = struct
access_tree access_tree
with with
| Some model -> | Some model ->
handle_model callee_pname astate_with_source model handle_model callee_pname astate_with_sink model
| None -> | None ->
apply_summary dummy_ret_opt actuals access_tree astate_with_source apply_summary dummy_ret_opt actuals access_tree astate_with_sink proc_data
proc_data call_site ) call_site ) )
in in
let astate_with_sanitizer = let astate_with_sanitizer =
match dummy_ret_opt with match dummy_ret_opt with

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

Loading…
Cancel
Save