[quandary] allow custom sources/sinks in C++

Reviewed By: akotulski

Differential Revision: D4975414

fbshipit-source-id: 4fd7078
master
Sam Blackshear 8 years ago committed by Facebook Github Bot
parent 548a36d71f
commit a02b37a03c

@ -12,8 +12,7 @@ open! IStd
module F = Format module F = Format
module L = Logging module L = Logging
module SourceKind = struct
module Kind = struct
type t = type t =
| EnvironmentVariable (** source that was read from an environment variable *) | EnvironmentVariable (** source that was read from an environment variable *)
| Other (** for testing or uncategorized sources *) | Other (** for testing or uncategorized sources *)
@ -22,19 +21,34 @@ module Kind = struct
let unknown = Unknown let unknown = Unknown
let of_string = function
| "EnvironmentVariable" -> EnvironmentVariable
| _ -> Other
let external_sources =
List.map
~f:(fun { QuandaryConfig.Source.procedure; kind; } ->
QualifiedCppName.Match.of_fuzzy_qual_names [procedure], kind)
(QuandaryConfig.Source.of_json Config.quandary_sources)
(* return Some(source kind) if [procedure_name] is in the list of externally specified sources *)
let get_external_source pname =
let qualified_pname = Typ.Procname.get_qualifiers pname in
List.find_map
~f:(fun (qualifiers, kind) ->
if QualifiedCppName.Match.match_qualifiers qualifiers qualified_pname
then Some (of_string kind)
else None)
external_sources
let get pname _ = match pname with let get pname _ = match pname with
| (Typ.Procname.ObjC_Cpp cpp_pname) as pname -> | Typ.Procname.ObjC_Cpp _ ->
begin get_external_source pname
match Typ.Procname.objc_cpp_get_class_name cpp_pname, Typ.Procname.get_method pname with | Typ.Procname.C _ ->
| "InferTaint", "source" -> Some Other
| _ -> None
end
| (Typ.Procname.C _) as pname ->
begin begin
match Typ.Procname.to_string pname with match Typ.Procname.to_string pname with
| "getenv" -> Some EnvironmentVariable | "getenv" -> Some EnvironmentVariable
| "__infer_taint_source" -> Some Other | _ -> get_external_source pname
| _ -> None
end end
| Typ.Procname.Block _ -> | Typ.Procname.Block _ ->
None None
@ -52,7 +66,7 @@ module Kind = struct
| Unknown -> F.fprintf fmt "Unknown" | Unknown -> F.fprintf fmt "Unknown"
end end
module CppSource = Source.Make(Kind) module CppSource = Source.Make(SourceKind)
module SinkKind = struct module SinkKind = struct
@ -61,27 +75,54 @@ module SinkKind = struct
| Other (** for testing or uncategorized sinks *) | Other (** for testing or uncategorized sinks *)
[@@deriving compare] [@@deriving compare]
let of_string = function
| "ShellExec" -> ShellExec
| _ -> Other
let external_sinks =
List.map
~f:(fun { QuandaryConfig.Sink.procedure; kind; index; } ->
QualifiedCppName.Match.of_fuzzy_qual_names [procedure], kind, index)
(QuandaryConfig.Sink.of_json Config.quandary_sinks)
(* taint the nth parameter (0-indexed) *)
let taint_nth n kind ~report_reachable =
[kind, n, report_reachable]
let taint_all actuals kind ~report_reachable =
List.mapi
~f:(fun actual_num _ -> kind, actual_num, report_reachable)
actuals
(* return Some(sink kind) if [procedure_name] is in the list of externally specified sinks *)
let get_external_sink pname actuals =
let qualified_pname = Typ.Procname.get_qualifiers pname in
List.find_map
~f:(fun (qualifiers, kind, index) ->
if QualifiedCppName.Match.match_qualifiers qualifiers qualified_pname
then
let kind = of_string kind in
try
let n = int_of_string index in
Some (taint_nth n kind ~report_reachable:true)
with Failure _ ->
(* couldn't parse the index, just taint everything *)
Some (taint_all actuals kind ~report_reachable:true)
else
None)
external_sinks
let get pname actuals _ = let get pname actuals _ =
let taint_all actuals kind ~report_reachable =
List.mapi
~f:(fun actual_num _ -> kind, actual_num, report_reachable)
actuals in
match pname with match pname with
| (Typ.Procname.ObjC_Cpp cpp_pname) as pname -> | Typ.Procname.ObjC_Cpp _ ->
begin Option.value (get_external_sink pname actuals) ~default:[]
match Typ.Procname.objc_cpp_get_class_name cpp_pname, Typ.Procname.get_method pname with
| "InferTaint", "sink:" -> taint_all actuals Other ~report_reachable:true
| _ -> []
end
| Typ.Procname.C _ -> | Typ.Procname.C _ ->
begin begin
match Typ.Procname.to_string pname with match Typ.Procname.to_string pname with
| "execl" | "execlp" | "execle" | "execv" | "execvp" -> | "execl" | "execlp" | "execle" | "execv" | "execvp" ->
taint_all actuals ShellExec ~report_reachable:false taint_all actuals ShellExec ~report_reachable:false
| "__infer_taint_sink" ->
[Other, 0, false]
| _ -> | _ ->
[] Option.value (get_external_sink pname actuals) ~default:[]
end end
| Typ.Procname.Block _ -> | Typ.Procname.Block _ ->
[] []

@ -29,7 +29,10 @@ module SourceKind = struct
| "UserControlledURI" -> UserControlledURI | "UserControlledURI" -> UserControlledURI
| _ -> Other | _ -> Other
let external_sources = QuandaryConfig.Source.of_json Config.quandary_sources let external_sources =
List.map
~f:(fun { QuandaryConfig.Source.procedure; kind; } -> Str.regexp procedure, kind)
(QuandaryConfig.Source.of_json Config.quandary_sources)
let get pname tenv = match pname with let get pname tenv = match pname with
| Typ.Procname.Java pname -> | Typ.Procname.Java pname ->
@ -70,9 +73,9 @@ module SourceKind = struct
(* 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 List.find_map
~f:(fun (source_spec : QuandaryConfig.Source.t) -> ~f:(fun (procedure_regex, kind) ->
if Str.string_match source_spec.procedure procedure 0 if Str.string_match procedure_regex procedure 0
then Some (of_string source_spec.kind) then Some (of_string kind)
else None) else None)
external_sources external_sources
end end
@ -193,7 +196,11 @@ module SinkKind = struct
| "StartComponent" -> StartComponent | "StartComponent" -> StartComponent
| _ -> Other | _ -> Other
let external_sinks = QuandaryConfig.Sink.of_json Config.quandary_sinks let external_sinks =
List.map
~f:(fun { QuandaryConfig.Sink.procedure; kind; index; } ->
Str.regexp procedure, kind, index)
(QuandaryConfig.Sink.of_json Config.quandary_sinks)
let get pname actuals tenv = let get pname actuals tenv =
(* taint all the inputs of [pname]. for non-static procedures, taints the "this" parameter only (* taint all the inputs of [pname]. for non-static procedures, taints the "this" parameter only
@ -278,12 +285,12 @@ module SinkKind = struct
(* check the list of externally specified sinks *) (* check the list of externally specified sinks *)
let procedure = class_name ^ "." ^ method_name in let procedure = class_name ^ "." ^ method_name in
List.find_map List.find_map
~f:(fun (sink_spec : QuandaryConfig.Sink.t) -> ~f:(fun (procedure_regex, kind, index) ->
if Str.string_match sink_spec.procedure procedure 0 if Str.string_match procedure_regex procedure 0
then then
let kind = of_string sink_spec.kind in let kind = of_string kind in
try try
let n = int_of_string sink_spec.index in let n = int_of_string index in
Some (taint_nth n kind ~report_reachable:true) Some (taint_nth n kind ~report_reachable:true)
with Failure _ -> with Failure _ ->
(* couldn't parse the index, just taint everything *) (* couldn't parse the index, just taint everything *)

@ -14,13 +14,13 @@ module F = Format
(** utilities for importing JSON specifications of sources/sinks into Quandary *) (** utilities for importing JSON specifications of sources/sinks into Quandary *)
module Source = struct module Source = struct
type t = { procedure : Str.regexp; kind : string; } type t = { procedure : string; kind : string; }
let of_json = function let of_json = function
| `List sources -> | `List sources ->
let parse_source json = let parse_source json =
let open Yojson.Basic in let open Yojson.Basic in
let procedure = Util.member "procedure" json |> Util.to_string |> Str.regexp in let procedure = Util.member "procedure" json |> Util.to_string in
let kind = Util.member "kind" json |> Util.to_string in let kind = Util.member "kind" json |> Util.to_string in
{ procedure; kind; } in { procedure; kind; } in
List.map ~f:parse_source sources List.map ~f:parse_source sources
@ -29,13 +29,13 @@ module Source = struct
end end
module Sink = struct module Sink = struct
type t = { procedure : Str.regexp; kind : string; index : string} type t = { procedure : string; kind : string; index : string}
let of_json = function let of_json = function
| `List sinks -> | `List sinks ->
let parse_sink json = let parse_sink json =
let open Yojson.Basic in let open Yojson.Basic in
let procedure = Util.member "procedure" json |> Util.to_string |> Str.regexp in let procedure = Util.member "procedure" json |> Util.to_string in
let kind = Util.member "kind" json |> Util.to_string in let kind = Util.member "kind" json |> Util.to_string in
let index = let index =
Util.member "index" json |> Util.to_string_option |> Option.value ~default:"all" in Util.member "index" json |> Util.to_string_option |> Option.value ~default:"all" in

@ -12,13 +12,13 @@ open! IStd
(** utilities for importing JSON specifications of sources/sinks into Quandary*) (** utilities for importing JSON specifications of sources/sinks into Quandary*)
module Source : sig module Source : sig
type t = { procedure : Str.regexp; kind : string; } type t = { procedure : string; kind : string; }
val of_json : [> `List of Yojson.Basic.json list ] -> t list val of_json : [> `List of Yojson.Basic.json list ] -> t list
end end
module Sink : sig module Sink : sig
type t = { procedure : Str.regexp; kind : string; index : string; } type t = { procedure : string; kind : string; index : string; }
val of_json : [> `List of Yojson.Basic.json list ] -> t list val of_json : [> `List of Yojson.Basic.json list ] -> t list
end end

@ -0,0 +1,37 @@
{
"quandary-sources": [
{
"procedure": "__infer_taint_source",
"kind": "Other"
},
{
"procedure": "basics::Obj::method_source",
"kind": "Other"
},
{
"procedure": "basics::Obj::static_source",
"kind": "Other"
},
{
"procedure": "basics::template_source",
"kind": "Other"
}
],
"quandary-sinks": [
{
"procedure": "__infer_taint_sink",
"kind": "Other",
"index": "0"
},
{
"procedure": "basics::Obj::method_sink",
"kind": "Other",
"index": "1"
},
{
"procedure": "basics::Obj::static_sink",
"kind": "Other",
"index": "0"
}
]
}

@ -14,7 +14,10 @@ namespace basics {
class Obj { class Obj {
public: public:
int field; void* method_source() { return (void*)0; }
void method_sink(void*) {}
static void* static_source() { return (void*)0; }
static void static_sink(void*) {}
}; };
void* returnSource() { return __infer_taint_source(); } void* returnSource() { return __infer_taint_source(); }
@ -43,4 +46,26 @@ void propagateBad() {
void* launderedSource = id(source); void* launderedSource = id(source);
callSink(launderedSource); callSink(launderedSource);
} }
// make sure specifying external sources/sinks as instance methods works
void object_source_sink_bad(Obj obj) {
void* source = obj.method_source();
obj.method_sink(source);
}
// make sure specifying external sources/sinks as static methods works
void static_source_sink_bad(Obj obj) {
void* source = Obj::static_source();
Obj::static_sink(source);
}
template <class T>
T* template_source() {
return nullptr;
}
void template_source_bad() {
void* source = template_source<void*>();
__infer_taint_sink(source);
}
} }

@ -1,7 +1,10 @@
codetoanalyze/cpp/quandary/basics.cpp, basics::object_source_sink_bad, 2, QUANDARY_TAINT_ERROR, [return from basics::Obj_method_source,call to basics::Obj_method_sink]
codetoanalyze/cpp/quandary/basics.cpp, basics::propagateBad, 3, QUANDARY_TAINT_ERROR, [return from __infer_taint_source,flow through basics::id,call to basics::callSink,call to __infer_taint_sink] codetoanalyze/cpp/quandary/basics.cpp, basics::propagateBad, 3, QUANDARY_TAINT_ERROR, [return from __infer_taint_source,flow through basics::id,call to basics::callSink,call to __infer_taint_sink]
codetoanalyze/cpp/quandary/basics.cpp, basics::returnSourceToSinkBad, 2, QUANDARY_TAINT_ERROR, [return from __infer_taint_source,return from basics::returnSource,call to __infer_taint_sink] codetoanalyze/cpp/quandary/basics.cpp, basics::returnSourceToSinkBad, 2, QUANDARY_TAINT_ERROR, [return from __infer_taint_source,return from basics::returnSource,call to __infer_taint_sink]
codetoanalyze/cpp/quandary/basics.cpp, basics::sourceThenCallSinkBad, 2, QUANDARY_TAINT_ERROR, [return from __infer_taint_source,call to basics::callSink,call to __infer_taint_sink] codetoanalyze/cpp/quandary/basics.cpp, basics::sourceThenCallSinkBad, 2, QUANDARY_TAINT_ERROR, [return from __infer_taint_source,call to basics::callSink,call to __infer_taint_sink]
codetoanalyze/cpp/quandary/basics.cpp, basics::sourceToSinkDirectBad, 2, QUANDARY_TAINT_ERROR, [return from __infer_taint_source,call to __infer_taint_sink] codetoanalyze/cpp/quandary/basics.cpp, basics::sourceToSinkDirectBad, 2, QUANDARY_TAINT_ERROR, [return from __infer_taint_source,call to __infer_taint_sink]
codetoanalyze/cpp/quandary/basics.cpp, basics::static_source_sink_bad, 2, QUANDARY_TAINT_ERROR, [return from basics::Obj_static_source,call to basics::Obj_static_sink]
codetoanalyze/cpp/quandary/basics.cpp, basics::template_source_bad, 2, QUANDARY_TAINT_ERROR, [return from basics::template_source<void_*>,call to __infer_taint_sink]
codetoanalyze/cpp/quandary/execs.cpp, execs::callExecBad, 6, QUANDARY_TAINT_ERROR, [return from getenv,call to execl] codetoanalyze/cpp/quandary/execs.cpp, execs::callExecBad, 6, QUANDARY_TAINT_ERROR, [return from getenv,call to execl]
codetoanalyze/cpp/quandary/execs.cpp, execs::callExecBad, 8, QUANDARY_TAINT_ERROR, [return from getenv,call to execl] codetoanalyze/cpp/quandary/execs.cpp, execs::callExecBad, 8, QUANDARY_TAINT_ERROR, [return from getenv,call to execl]
codetoanalyze/cpp/quandary/execs.cpp, execs::callExecBad, 11, QUANDARY_TAINT_ERROR, [return from getenv,call to execl] codetoanalyze/cpp/quandary/execs.cpp, execs::callExecBad, 11, QUANDARY_TAINT_ERROR, [return from getenv,call to execl]

Loading…
Cancel
Save